Changing multiple button color separately when clicked - javascript

I'm having troubles with my javascript code, I have a snippet that creates a number of button depending on the loop range, the buttons share the same class but the ids are different
<div class="panel-footer" id="loop">
<ul class="post-action">
{% for i in range %}
<button class="btn btn-success guess" id="{{ i }}" value="{{ i }}" onclick="transferField(this.value)">{{ i }} </button>
{% endfor %}
</ul>
</div>
I am trying to change the color of each button when it is clicked and when clicked again changes back to default color but its not working fine, when i click on button 5 it changes color but once i click on button 6, it wont change color until i click button 6 or click another button again. Here is the js code:
<script>
clicked = true;
$(".guess").click(function(){
xyz = this.id
console.log(xyz)
if(clicked){
$('#' + this.id).css('background-color', '#FF8E2B');
clicked = false;
} else {
$('#' + this.id).css('background-color', '#27AE60');
clicked = true;
}
});
</script>
What have i done wrong?

Your problem is you are using clicked as a global variable.
Just store clicked in this.
$(".guess").click(function(){
xyz = this.id
console.log(xyz)
if(this.clicked){
$(this).css('background-color', '#FF8E2B');
this.clicked = false;
} else {
$(this).css('background-color', '#27AE60');
this.clicked = true;
}
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="panel-footer" id="loop">
<ul class="post-action">
<button class="btn btn-success guess" id="one" value="one" > one</button>
<button class="btn btn-success guess" id="two" value="two" > two</button>
<button class="btn btn-success guess" id="three" value="three" > three</button>
</ul>
</div>

You are trying to "track" the state of multiple buttons using a single var, of course this wont work
try the following instead
$(".guess").click(function(){
var $this = $(this);
var clicked = $this.data('clicked');
if(clicked) {
$this.css('background-color', '#FF8E2B');
} else {
$this.css('background-color', '#27AE60');
}
$this.data('clicked', !clicked);
});

You can store an array of color for each element at data-* of element, use .data(), Array.prototype.reverse() to toggle to array, set background to element at index 0 of array
$("button").on("click", function() {
$(this).css("background", $(this).data().colors.reverse()[0])
})
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<button data-colors='["#FF8E2B", "#27AE60"]' style="background:#FF8E2B">click</button>

What have i done wrong?
The main issue you have with your code is that you have one variable keeping track of the clicked state of multiple button elements.
The way to fix this is to add state to the element itself essentially giving multiple variables for multiple elements.
I'm doing this by adding an event object to the callback of the click handler and getting event.currentTarget. The value of event.currentTarget is the element object being clicked. You can add state to this object just like you'd add state to another other javascript object
event.currentTarget.clicked = true;
Now you can keep track of the state for each element!
// just some simple code to get your template in pure JS, don't worry about the code here
const range = [0,1,2,3,4,5];
const template = `<div class="panel-footer" id="loop">
<ul class="post-action">
${range.map(i => `
<button class="btn btn-success guess" id="${i}" value="${i}">${i}</button>
`)}
</ul>
</div>`;
const div = document.createElement('div');
div.innerHTML = template;
document.body.appendChild(div);
// here are where the changes start
// var clicked = true; instead of having a global variable to keep track of the state of all your buttons
$(".guess").click(function(event) {
// you need local state attached to the button
// here we're getting the button element being clicked
const currentTarget = event.currentTarget;
xyz = this.id
console.log(xyz);
// if clicked is truthy
if (currentTarget.clicked) {
$('#' + this.id).css('background-color', '#FF8E2B');
currentTarget.clicked = false;
} else {
$('#' + this.id).css('background-color', '#27AE60');
// set on the element some state
currentTarget.clicked = true;
}
});
.guess {
/* start out in the orange state */
background-color: #FF8E2B;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

Related

How to refactor my code so it's not repeating the same thing

I have 3 different buttons on my page, and when you click on one it checks the corresponding radio button.
Currently, I have each button with its own onclick function:
onclick="radioChecked1()"
onclick="radioChecked2()"
onclick="radioChecked2()"
And then there are the functions:
function radioChecked1() {
var package1 = document.querySelector("#package1");
package1.setAttribute("checked", 1);
}
function radioChecked2() {
var package2 = document.querySelector("#package2");
package2.setAttribute("checked", 1);
}
function radioChecked3() {
var package3 = document.querySelector("#package3");
package3.setAttribute("checked", 1);
}
These functions are doing the same thing, the only thing that changes is the number in the id of the input it's selecting.
I'm sure there's a way to simplify this into one function instead of a separate one for each button, I don't know how to do it.
You can refactor this code by simplifying the function and using parameters:
function radioChecked(id) {
var package = document.querySelector(id);
package.setAttribute("checked", 1);
}
Then on your buttons call the function with the corresponding id:
onclick="radioChecked('#package1')" onclick="radioChecked('#package2')" onclick="radioChecked('#package3')"
It depends a little on how your markup is written but if you add data attributes to both buttons and the radio buttons you can take the id from a button when it's clicked, and then find the corresponding id on a radio input.
Here I've wrapped the buttons and radio input in their own containers so that I can use event delegation - adding one listener to a parent container that catches events from its child elements as they bubble up the DOM, rather than attaching listeners to all of the elements.
// Instead of inline JS we cache the containers elements
// and then add one listener to the buttons container
const radios = document.querySelector('.radios');
const btns = document.querySelector('.btns');
btns.addEventListener('click', handleClick);
// If the clicked element is a button
// extract its id from its dataset, look for
// the corresponding radio input, and update the
// `checked` property
function handleClick(e) {
if (e.target.matches('button')) {
const { id } = e.target.dataset;
const radio = radios.querySelector(`[data-id="${id}"]`);
radio.checked = true;
}
}
fieldset { margin-top: 1em; }
<fieldset class="btns">
<legend>Buttons</legend>
<button data-id="radio1" type="button">Button 1</button>
<button data-id="radio2" type="button">Button 2</button>
<button data-id="radio3" type="button">Button 3</button>
</fieldset>
<fieldset class="radios">
<legend>Radio buttons</legend>
<label for="radio1">Radio1
<input data-id="radio1" type="radio" name="radioset">
</label>
<label for="radio2">Radio2
<input data-id="radio2" type="radio" name="radioset">
</label>
<label for="radio3">Radio3
<input data-id="radio3" type="radio" name="radioset">
</label>
</fieldset>
Additional documentation
querySelector
addEventListener
matches
Destructuring assignment

Change color of all clicked buttons

I have a set of buttons in an html page of the following form:
<button
id="testID"
mat-mini-fab
ngClass="list-button"
(click)="onClick($event)"
>
Press
</button>
I try to change the color of each button belonging to .list-button class after clicking on it using the following css code:
.list-button:focus {
background-color: #7d698d;
}
However, while the color of the button I click each time changes
(the color of all the previously clicked buttons also changes back to their original color).
How could I fix it? I want all the clicked buttons to remain their new color.
I also tried assigning an id to the buttons of this class and changing their color inside the onClick() method as follows without success. The same problem remains. Could you help me, please?
onclick(event: any) {
const btn = document.getElementById('testID');
if (btn) btn.style.backgroundColor = '#7d698d';
}
ID must be unique
You can use the function and pass el which will refer to this as an argument function onClick(el){......} and use it onClick(this)
No need to get the id you can directly use el.style or event.target.style see the next examples
function onClick(el){
el.style.backgroundColor = '#7d698d';
}
<button
id="testID-1"
mat-mini-fab
ngClass="list-button"
onclick="onClick(this)"
>
Press
</button>
<button
id="testID-2"
mat-mini-fab
ngClass="list-button"
onclick="onClick(this)"
>
Press
</button>
Also You can use the function and pass event which equal event as an argument function onClick(event){......} and use it onClick(event)
function onClick(event){
event.target.style.backgroundColor = '#7d698d';
}
<button
id="testID-1"
mat-mini-fab
ngClass="list-button"
onclick="onClick(event)"
>
Press
</button>
<button
id="testID-2"
mat-mini-fab
ngClass="list-button"
onclick="onClick(event)"
>
Press
</button>
You need to get the id of the button that fired the event
onclick(event: any) {
const btn = document.getElementById(event.target.id);
if (btn) btn.style.backgroundColor = '#7d698d';
}
The reason why the color of the previously clicked buttons is resetting after you click on another one is that the focus is set on the last clicked button. Try using the code in the Venkatesh's answer.
No need for js. Use the focus selector in your stylesheet.
The :focus selector is allowed on elements that accept keyboard events or other user inputs.
~ W3
button.list-button:focus {
background-color: #7d698d;
color: white;
}
<button class="list-button">Click</button>
Edit ~ with js. This method uses .querySelectorAll("button") and toggles the styled class .focus each time the button(s) are clicked. This works well when having multiple buttons.
// Get all the elements on the basis of query selector (button)
let btn = document.querySelectorAll("button");
for (var i = 0; i < btn.length; i++) {
(function(index) {
btn[index].addEventListener("click", function() {
console.log("Clicked Button: " + index);
let isPresent = false;
// Checks if the class is present or not
this.classList.forEach(function(e, i) {
if (e == "focus") {
isPresent = true;
} else {
isPresent = false;
}
});
// Toggles the presence of class (.focus) on the basis of the isPresent variable
if (isPresent) {
this.classList.remove("focus");
} else {
this.classList.add("focus");
}
});
})(i);
}
.focus {
background-color: #7d698d;
color: white;
cursor: pointer;
}
<button id="btn">Click</button>
<button id="btn">Click</button>
<button id="btn">Click</button>

Satisfying button click condition to bring up a message in javascript

Sorry in advance if my code is really bad but I am just a beginner. I would like to create a word search puzzle using buttons. When the person is finished finding by clicking on all of the words which I am going to make from buttons I want a message to come up that they have completed the puzzle. So I created a sample here with 4 buttons but I can't seem to get my code to work. I want the message to come up in the div container once all the buttons have been clicked on. Am I on the right track here or way off? Any insight would be so much appreciated!
<html>
<p onclick="myFunction()" id="1" value=false >Button1</p>
<p onclick="myFunction()" id="2" value=false >Button2</p>
<p onclick="myFunction()" id="3" value=false >Button3</p>
<p onclick="myFunction()" id="4" value=false >Button4</p>
<div id="demo">Message displays here if all 4 buttons are clicked</div>
<script>
function myFunction(){
value = true;}
if(p 1){
value = true;}
if(p 2){
value = true;}
if(p 3){
value = true;}
if(p 4){
value = true;}
else{
document.getElementById("demo").innerHTML = "Congratulations you clicked on all of the buttons";}
}
</script>
</html>
You could use the buttons' data attributes to hold their state. (Note: for a more complex project, you probably don't want to do this)
Also, putting JS inline like onclick="myfunction()" is somewhat bad for your code - it encourages globals and makes JS logic harder to follow. So, I've shown an alternative using an IIFE, .querySelectorAll(), and .addEventListener():
// IIFE to keep variables out of the global scope
;(() => {
// NOTE: These are more flexible when they are arrays (thus, Array.from())
const btnEls = Array.from(document.querySelectorAll('.js-button'))
const msgEls = Array.from(document.querySelectorAll('.js-message'))
const handleButtonClick = ({ target: btnEl }) => {
btnEl.disabled = true // optional
btnEl.dataset.toggled = 'true' // using the DOM to hold data
if (btnEls.some(el => el.dataset.toggled !== 'true')) return
msgEls.forEach(el => {
el.textContent = 'Congratulations you clicked on all of the buttons'
})
}
// Our "onclick" equivalent
btnEls.forEach(el => el.addEventListener('click', handleButtonClick))
})()
<button class="js-button">Button1</button>
<button class="js-button">Button2</button>
<button class="js-button">Button3</button>
<button class="js-button">Button4</button>
<p class="js-message">Message displays here if all 4 buttons are clicked</p>
...There's probably a lot of syntax there you don't know but that example should be helpful for those learning from a more modern source. Since you're learning from something that uses older JS syntax, here's some older JS code that works about the same (but isn't as easy to maintain):
// IIFE to keep variables out of the global scope
;(function () {
// NOTE: These are more flexible when they are arrays (thus, Array.from())
var btnEls = Array.from(document.querySelectorAll('.js-button'))
var msgEls = Array.from(document.querySelectorAll('.js-message'))
function handleButtonClick(event) {
var btnEl = event.target
btnEl.disabled = true // optional
btnEl.dataset.toggled = 'true' // using the DOM to hold data
if (btnEls.some(function (el) {
return el.dataset.toggled !== 'true'
})) return
msgEls.forEach(function (el) {
el.textContent = 'Congratulations you clicked on all of the buttons'
})
}
// Our "onclick" equivalent
btnEls.forEach(function (el) {
el.addEventListener('click', handleButtonClick)
})
})()
<button class="js-button">Button1</button>
<button class="js-button">Button2</button>
<button class="js-button">Button3</button>
<button class="js-button">Button4</button>
<p class="js-message">Message displays here if all 4 buttons are clicked</p>
Here was my solution.
var set = []; //decalre an empty array
function myFunction(Id) {
console.log(Id); //the Id will be the vlaue from the button. For example button 1 has an Id of one as passed into by 'myFunction(1)
if (set.indexOf(Id) == -1) { //here we check to see if the Id number is in the array
set.push(Id); //if it's not in the array, we add it in
console.log(set);
console.log("length: " + set.length);
if (set.length > 3) { //if the lengthof the array is greater than three, all 4 buttons have been clicked.
document.getElementById("demo").innerHTML = "Congratulations you clicked on all of the buttons";
}
}
}
<p onclick="myFunction(0)">Button0</p>
<p onclick="myFunction(1)">Button1</p>
<p onclick="myFunction(2)">Button2</p>
<p onclick="myFunction(3)">Button3</p>
<div id="demo">Message displays here if all 4 buttons are clicked</div>
An easier way of doing this is with an event listener that listens to each button click, then makes the value of that button true, and then checks all the buttons to see if they are all clicked and if so output the congrats message
HTML
added classes to each button and removed the onclick function
<html>
<p class='button' id="1" value=false >Button1</p>
<p class='button' id="2" value=false >Button2</p>
<p class='button' id="3" value=false >Button3</p>
<p class='button' id="4" value=false>Button4</p>
<div id="demo">Message displays here if all 4 buttons are clicked</div>
</html>
JS
window.addEventListener('click', (e)=>{
var bool = true
if (e.target.classList.contains('button')) {
e.target.setAttribute('value', "true")
}
buttons = document.querySelectorAll('.button')
for (let i = 0; i < buttons.length; i++) {
console.log(buttons[i].getAttribute('value'))
if (buttons[i].getAttribute('value') == "false") {
bool = false
}
}
if (bool == true) {
document.getElementById("demo").innerHTML = "Congratulations you clicked on all of the buttons";
}
})
I would suggest this as an easy answer to understand javascript and html a little better:
HTML
<html>
<body>
<button onclick="myFunction(event)" name="button1">Button1</button>
<button onclick="myFunction(event)" name="button2">Button2</button>
<button onclick="myFunction(event)" name="button3">Button3</button>
<button onclick="myFunction(event)" name="button4">Button4</button>
<div id="demo">Message displays here if all 4 buttons are clicked</div>
</body>
</html>
JS
var foundButtons = {
button1: false,
button2: false,
button3: false,
button4: false,
};
function myFunction(event) {
foundButtons[event.target.name] = true;
for (var button in foundButtons) {
if (foundButtons.hasOwnProperty(button)) {
if (!foundButtons[button]) {
return
}
}
}
document.getElementById("demo").innerHTML = "Congratulations you clicked on all of the buttons";
}
What this does is that you have an object of the buttons or rather the words that must be clicked to show the message. Now when one of the buttons gets clicked, its property gets set to true. Then it iterates over the properties and ends the function with a return statement, if it finds a false value, which means there is a button that has not been clicked. When the function does not get stopped it will show the success message.

Get dynamically created element that was click javascript

I'm trying to track what buttons users click via javascript/jquery. I have the following code
$(document).on('click', '[data-tracker]', function( event ) {
var target = event.target;
var targetName = $(target).data('tracker');
console.log(targetName);
});
<button data-tracker="test1">test1</button>
<i data-tracker="test2">test2</i>
<img data-tracker="test3">
At the moment this works as I want it to. When someone clicks on an element on the page that has the data-tracker attribute I log the value in the console.
I'm using datatables on some pages which dynamically creates elements from json returned from the server. I can't figure out how to record elements that have been dynamically created.
So all in all I want 1 function that will check if a user clicks on an element with the data-tracker attribute and output it's value to the console.
First of all instead of something like this
$(document).on('click', '[data-tracker]', function( event ) {
var target = event.target;
var targetName = $(target).data('tracker');
console.log(targetName);
});
You can do this
$(document).on('click', '[data-tracker]', function() {
var targetName = $(this).data('tracker');
console.log(targetName);
});
Secondly, the reason of this behavior may be because .data() function works that way
Store arbitrary data associated with the specified element. Returns the value that was set.
So when you dynamically add an element with attribute data-tracker there is no value set because it was not stored. So instead of using .data() just use .attr().
$(document).on('click', '[data-tracker]', function() {
var targetName = $(this).attr('data-tracker');
console.log(targetName);
});
Here is a snippet
$(document).on('click', '[data-tracker]', function() {
console.log($(this).attr('data-tracker'));
});
var num = 0;
$("#addElement").on('click', function() {
$("<div>").attr('data-tracker', 'test value ' + num).html('Test value' + num).appendTo(".content");
num++;
});
[data-tracker] {
cursor: pointer;
color: #F00;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<button id="addElement">Add element</button>
<div class="content">
<div data-tracker="value1">Value 1</div>
<div data-tracker="value2">Value 2</div>
<div>Test</div>
<div data-tracker="value3">Value 3</div>
<div data-tracker="value4">Value 4</div>
</div>
Your code looks OK to me. You are probably making some mistake when added elements with JSON, make sure you get that correct.
Here I've added a button to add elements to the page dynamically, you can verify
$(document).on('click', '[data-tracker]', function( event ) {
var target = event.target;
var targetName = $(target).data('tracker');
console.log(targetName);
});
$("#new").click(function(){
$("#d").append(`<button data-tracker="test1">test1</button>
<i data-tracker="test2">test2</i>
<img data-tracker="test3">`)
})
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<div id="d">
<button data-tracker="test1">test1</button>
<i data-tracker="test2">test2</i>
<img data-tracker="test3">
</div>
<br/>
<button id="new">Add</button>
I am assuming the data-tracker is a class name. And here is a example of how you adding a onclick event to all element with the same class name
a = document.getElementsByClassName("data-tracker")
for (var key in a) {
if (a.hasOwnProperty(key) && Number.isInteger(parseInt(key))) {
a[key].addEventListener("click", function(){console.log('hi')});
}
}
<input type="button" class="data-tracker" value="button1"></input>
<input type="button" class="" value="button2"></input>
<input type="button" class="data-tracker" value="button3"></input>

How to assign a javascript function by classname

I have multiple buttons (generated by php) for a shopping cart application:
<button class="addtocart" id="<?php echo $code; ?>">
<span id="addtocartbutton">Add to cart</span>
</button>
I want to update my cart using a function:
function AddtoCart() {
alert("Added!");
}
Later, I want to find the id ($code) created by the button which called it (not sure how to do that also, but maybe that's another question). And so I tried this:
document.getElementsByClassName("addtocart").addEventListener("click", AddtoCart());
But it doesn't work. It was working using an onclick, but I understand that the right way to do it by creating an EventListener. Also, I cannot use the on() function in jQuery, because I am forced to use jQuery Version 1.6 which does not have it.
I have looked at https://stackoverflow.com/a/25387857/989468 and I can't really assign it to the parent which is a p tag, because I obviously don't want the other elements in the p tag to be assigned this function.
While the answers given are correct, there is another way: Event Delegation
Attach the listener to a SINGLE thing, in this case the document body and then check to see what element was actually clicked on:
Warning: Typed on the fly: Untested
// Only needed *once* and items may be added or removed on the fly without
// having to add/remove event listeners.
document.body.addEventListener("click", addtoCart);
function addtoCart(event) {
var target = event.target;
while(target) {
if (target.classList.contains('addtocart')) {
break;
}
// Note: May want parentElement here instead.
target = target.parentNode;
}
if (!target) {
return;
}
var id = target.dataset.id;
alert(id + " added!");
}
You should attach click event to every element with class addtocart, since getElementsByClassName() return an array of all objects with given class name so you could use for to loop through everyone of them and associate it with function you want to trigger on click (in my example this function called my_function), check example bellow :
var class_names= document.getElementsByClassName("addtocart");
for (var i = 0; i < class_names.length; i++) {
class_names[i].addEventListener('click', my_function, false);
}
Hope this helps.
function my_function() {
alert(this.id);
};
var class_names= document.getElementsByClassName("addtocart");
for (var i = 0; i < class_names.length; i++) {
class_names[i].addEventListener('click', my_function, false);
}
<button class="addtocart" id="id_1">button 1</button>
<button class="addtocart" id="id_2">button 2</button>
<button class="addtocart" id="id_3">button 3</button>
<button class="addtocart" id="id_3">button 4</button>
I'll show some of the errors you had in your code, then I'll show you how can you improve it so that you can achieve what you want, and I also show that it works with buttons dynamically added later:
First and foremost, you need to pass the function reference (it's name) to the addEventListener! You have called the function, and passed whatever it returned. Instead of:
document.getElementsByClassName("addtocart").addEventListener("click", AddtoCart());
It should've been:
document.getElementsByClassName("addtocart").addEventListener("click", AddtoCart);
Second: document.getElementsByClassName("addtocart") returns a NodeList, you can't operate on it, you need to operate on it's elements: document.getElementsByClassName("addtocart")[0], [1],....
Third, I would suggest you to use the data-... html attribute:
<button class="addtocart" id="addtocart" data-foo="<? echo $code; ?>">
This way you can pass even more data. Now you can get the $code as:
document.getElementById('addtocart').dataset.foo
// el: the button element
function AddtoCart(el) {
// this is the id:
var id = el.id;
// and this is an example data attribute. You can have as many as you wish:
var foo = el.dataset.foo;
alert(id + " (" + foo + ") added!");
}
// Try add a div or something around the area where all the buttons
// will be placed. Even those that will be added dynamically.
// This optimizes it a lib, as every click inside that div will trigger
// onButtonClick()
document.getElementById("buttons").addEventListener("click", onButtonClick);
// this shows that even works when you dynamically add a button later
document.getElementById('add').onclick = addButton;
function addButton() {
var s = document.createElement("span");
s.text = "Add to cart";
var b = document.createElement("button");
b.innerHTML = 'Third <span class="addtocartbutton">Add to cart</span>';
b.className = "addtocart";
b.id="third";
b.dataset.foo="trio";
// note the new button has the same html structure, class
// and it's added under #buttons div!
document.getElementById("buttons").appendChild(b);
}
// this will gett triggered on every click on #buttons
function onButtonClick(event) {
var el = event.target;
if (el && el.parentNode && el.parentNode.classList.contains('addtocart')) {
// call your original handler and pass the button that has the
// id and the other datasets
AddtoCart(el.parentNode);
}
}
<div id="buttons">
<button class="addtocart" id="first" data-foo="uno">First <span class="addtocartbutton">Add to cart</span></button>
<button class="addtocart" id="second" data-foo="duo">Second <span class="addtocartbutton">Add to cart</span></button>
</div>
<button id="add">Add new button</button>
<html>
<head>
<script>
window.onload=function{
var btn = document.getElementsByName("addtocartbtn")[0];
btn.addEventListener("click", AddtoCart());
}
function AddtoCart() {
alert("Added!");
}
</script>
</head>
<body >
<button class="addtocart" name ="addtocartbtn" id="<?php echo $code; ?>" > <span id="addtocartbutton">Add to cart</span></button>
</body>
</html>
Actually class in Javascript is for multiple selection you should provide index like an array.
<button class="addtocart"> <span id="addtocartbutton">Add to cart</span></button>
<script type="text/javascript">
document.getElementsByClassName("addtocart")[0].addEventListener("click", AddtoCart);
function AddtoCart() {
alert("Added!");
}
</script>
Also your second parameter was wrong don't use parentheses.
Applying parentheses means it will call the function automatically when loaded, and will not call the function after that.

Categories

Resources