I have many buttons that have class="clearSelect". I want these buttons the execute a function onclick. I am new to javascript and not quite sure why this is occurring but I think my functions are being executed instead of only executing onclick
The code below is what is calling all my other functions causing every button to be clicked.
code:
var buttons = document.getElementsByClassName("clearSelect"); // objects with class="clearSelect"
for (var i = 0; i < buttons.length; i++) {
var button = buttons[i];
// button.addEventListener("onclick", clearOrSelectAll(button.id));
button.onclick = clearOrSelectAll(button.id);
}
These are the functions being called:
function clearOrSelectAll(btn) {
var cleartab = clearButtonSet[btn];
var selecttab = selectButtonSet[btn];
// console.log("clicked!");
if (cleartab != null) {
getOSList(cleartab, false);
} else {
getOSList(selecttab, true);
}
}
function getOSList(tabVal, fate) {
var configTab = document.getElementById(tabVal);
var browserList = configTab.getElementsByClassName("browser_list");
// var idObjs = browserList.getElementsByTagName("li");
for (var h = 0; h < browserList.length; h++) {
var idObjs = browserList[h].getElementsByTagName("li");
// console.log(h);
// console.log(idObjs);
// select all
if (fate) {
for (var i = 0; i < idObjs.length; i++) {
if (configs[idObjs[i].id] == null) {
idObjs[i].className = "selected";
configs[idObjs[i].id] = config_dictionary[idObjs[i].id];
}
}
// clear all
} else {
for (var j = 0; j < idObjs.length; j++) {
if (configs[idObjs[j].id] == null) {
idObjs[j].className = "unselected";
delete configs[idObjs[j].id];
}
}
}
}
}
#Christopher was very close, but button.id should be this.id.
button.onclick = function() {
clearOrSelectAll(this.id);
}
The reason button.id doesn't work can be demonstrated with this code:
var buttons= document.getElementsByTagName('button');
for (var i = 0; i < buttons.length; i++) {
var button = buttons[i];
button.onclick = function() {
alert(button.id);
}
}
<button id="B1">Button 1</button>
<button id="B2">Button 2</button>
<button id="B3">Button 3</button>
Each button returns "B3," because that's the last object that the variable button is assigned to.
In your for loop when you attach the event to all of the buttons, you are calling the clearOrSelectAll function. You probably want to wrap it in an anonymous function to make sure it's only called when the event is fired.
// Non-ideal solution: see edit
button.onclick = function() {
clearOrSelectAll(button.id);
}
EDIT: It has been pointed out that the 'this' context variable will point to the element in question when an event handler is attached by means of the onclick property, or the addEventListener method. As such it would probably be cleaner (and easier to read) if you were to reference that instead of using 'button' as a closure and count on javascript engines to not optimize your loop too heavily (as that would mess with the value of 'button' at the time that the event is called.
button.onclick = function() {
clearOrSelectAll(this.id);
};
Related
hello I am struggling to use JS in order to make the buttons on my HTML page add a border to the button when it is clicked and to remove the border when it is clicked again. it works for the first 2 clicks but then no longer does anything after that. please excuse my js im extremely inexperienced.
JavaScript:
<script>
var flag = true;
var buttons = document.getElementsByClassName("btn");
function buttonFunction() {
if (flag) {
for (var i = 0; i < buttons.length; i++) {
document.getElementsByClassName("btn")[i].addEventListener("click", function() {
this.classList.add("buttonSelect");
flag = false
return
});
}
} else {
if (flag == false) {
for (var i = 0; i < buttons.length; i++) {
document.getElementsByClassName("btn")[i].addEventListener("click", function() {
this.classList.add("buttonUnselect");
flag = true
return
});
}
}
}
}
</script>
The real issue is you're adding both classes and never removing them. Get rid of the if else statement and just toggle the class on click. Don't need to wrap the loop in a function either. Just let the javascript execute the event listeners at runtime.
Also, make use of the buttons var you created instead of trying to query the DOM again for the same elements.
<script>
var buttons = document.getElementsByClassName("btn");
for (var i = 0; i < buttons.length; i++) {
buttons[i].addEventListener("click", function() {
this.classList.toggle("buttonSelect");
})
}
</script>
I've been having issues where when I try to remove an event from the buttons it seems to only be removing the event for the one-button even though I have looped through the buttons and removed the event.
thank you.
function ChangeQuestions() {
let currentQuestion = getQuestion(); //another function to get the question from an array - returns an object with questions, answers and correctAnswer
const correctAnswer = currentQuestion.correct;
console.log(currentQuestion);
if (questionsArray.length === 0) {
//If the array is empty push the questions again
questionsArray.push(firstQuestion, secondQuestion, thirdQuestion);
}
document.querySelector('.question-header').textContent = currentQuestion.questions;
for (let i = 1; i < 4; i++) {
document.querySelector('.btn-Ans-' + i).textContent = currentQuestion.answers[i - 1];
document.querySelector('.btn-Ans-' + i).addEventListener('click', function checkAns(e) {
if (e.target.innerHTML === correctAnswer) {
score++;
console.log(score);
removeEvent('click', checkAns);
ChangeQuestions();
} else {
console.log(score);
removeEvent('click', checkAns);
ChangeQuestions();
}
});
}
}
function removeEvent(event, func) {
for (let i = 1; i < 4; i++) {
document.querySelector('.btn-Ans-' + i).removeEventListener(event, func);
}
}
With
for (let i = 1; i < 4; i++) {
document.querySelector('.btn-Ans-' + i).addEventListener('click', function checkAns(e) {
A new checkAns function is created inside every iteration of the loop, and removeEventListener must be passed the exact same function that addEventListener was called with. Since the different loop iterations have different functions passed into their respective addEventListener calls, the removeEvent function appears to only affect the element that was clicked, and none of the rest.
Here's a more minimal example:
const fns = [];
for (let i = 0; i < 2; i++) {
const foo = () => console.log('foo');
fns.push(foo);
window.addEventListener('click', foo);
}
// Not the same function:
console.log(fns[0] === fns[1]);
I'd add just a single listener to the container instead, and use event delegation to check which element was clicked on:
btnContainer.addEventListener('click', function handler(e) {
if (!e.target.matches('[class^="btn-Ans"]')) {
return;
}
btnContainer.removeEventListener('click', handler);
if (e.target.innerHTML === correctAnswer) {
score++;
}
console.log(score);
ChangeQuestions();
});
where btnContainer is a container for your btn-Ans-s.
On the first click on each section I create an li which contains the content of that section h2 and it has to be an anchor to that section. The problem is that item.onclick fires in the doSomething method instead of when I click the li.
How can I fix it?
PS: I know that the first child of each section is a h2.
var list = document.getElementById('cuprins');
var sections = document.getElementsByTagName('section');
var l = sections.length;
var viz = new Array(l).fill(0);
for(var i = 0; i < l; i++)
sections[i].addEventListener('click', doSomething);
function jump(h){
var top = h.offsetTop;
window.scrollTo(0, top);
console.log(h);
}
function doSomething(e){
var index;
for(var i = 0; i < l; i++)
if(e.currentTarget == sections[i]){
index = i;
break;
}
if(viz[index] == 0){
var text = sections[index].children[0].textContent;
var item = document.createElement('li');
item.innerHTML = text;
item.onclick = jump(sections[index]);
list.appendChild(item);
viz[index] = 1;
}
}
The problem is that jump(sections[index]); is not being assigned as the callback function for item.click. Instead it is being executed anytime doSomething is called and viz[index] == 0.
What then happens is that the return value from calling jump(sections[index]); is being assigned as the onclick of item, but jump doesn't return any value, so there is no function registered with onclick.
You need to have jump invoked when item is clicked, so you need the line to be:
item.onclick = function() { jump(sections[index]); };
So that the outer function is assigned as the event callback and the contents of the function aren't invoked until the event occurs.
Having said that, I would move away from onXyz event properties and move to modern, standards-based code with addEventListener():
item.addEventListener("click", function() { jump(sections[index]); });
You're executing function and assign the result to item.onlick event
Unstead, using the code below will assign function declaration to item.onclick event
if(viz[index] == 0){
var text = sections[index].textContent;
var item = document.createElement('li');
item.innerHTML = text;
item.onclick = function() {
jump(sections[index])
};
list.appendChild(item);
viz[index] = 1;
}
}
I'm building a simple calculator app. I'm trying to accomplish three things:
Assign an event listener to the buttons.
When the button is clicked, fire an event.
Use the eventListener function to display the value of the clicked button.
for (i = 0; i < btn.length; i++) {
var btnVal = btn[i].value;
btn[i].addEventListener("click", function() { displayNumber(btnVal) }, false);
}
function displayNumber(param) {
displayedNum.innerHTML = param;
}
It seems btnVal is not accessible when passed to the event listener function.
The assignment won't work like that. Instead, use the target's value
for (i = 0; i < btn.length; i++) {
btn[i].addEventListener("click", function(e) {
displayNumber(e.target.value)
}, false);
}
Defining event listeners (or other asynchronous things) inside of a loop is tricky. You may think you're creating several different btnVal variables, one for each time through the loop, but you're not. That var btnVal gets hoisted to the top and reused, so your code ends up behaving like this:
var btnVal;
for (i = 0; i < btn.length; i++) {
btnVal = btn[i].value;
btn[i].addEventListener("click", function() { displayNumber(btnVal) }, false);
}
So all of your event listeners are interacting with the very same variable, and when they eventually get clicked, they'll only see the very last value that was assigned to btnVal, which should be btn[btn.length -1].value. All the values earlier in the array are lost.
There are a few ways you can address this:
1) Rather than depending on a closure variable, you could pull it from the element itself when the event goes off.
for (i = 0; i < btn.length; i++) {
btn[i].addEventListener("click", function (event) {
displayNumber(event.target.value);
});
}
2) Move the event listener creation into a function, and pass btnVal in. Since it's now a function parameter, it gets a new binding.
for (i = 0; i < btn.length; i++) {
createListener(btn[i], btn[i].value);
}
function createListener(element, val) {
element.addEventListener("click", function () { displayNumber(val) }, false);
}
3) you can do it inline using an IIFE.
for (i = 0; i < btn.length; i++) {
(function (button) {
button.addEventListener("click", function () { displayNumber(button.value) }, false);
})(btn[i]);
}
EDIT: added option 4
4) If you can use ES2015, use let. Let has block scope, and will get a new binding each time through the loop
for (i = 0; i < btn.length; i++) {
let btnVal = btn[i].value;
btn[i].addEventListener("click", function() { displayNumber(btnVal) }, false);
}
You cant use btnVal inside the event.
It should look like this
for (i = 0; i < btn.length; i++) {
btn[i].addEventListener("click", function(event) {
var clickedButton = event.target || event.srcElement;
console.log(clickedButton.value)
}, false);
}
I want change color title on click it.
I write next:
window.onload = function () {
var color = document.getElementById('input-color');
var elems = document.querySelectorAll('.title');
function chancheColor(){
for (var i = 0; i < elems.length; i++) {
elems[i].addEventListener('click', function () {
color.click();
color.addEventListener('change', function () {
var name = this.value;
for (var i = 0; i < elems.length; i++) {
elems[i].style.color = name;
}
});
});
}
}
chancheColor();
};
<input id="input-color" type="color">
<p class="title">First</p>
<p class="title">First</p>
<p class="title">First</p>
I simulate a click on
color.click();
Is it true?
Now my function сhange the color for all titles
beacause I have a second cycle for (var i = 0; i < elems.length; i++)
I don't know, how get the current value of the title which was clicked.
And generally how is it possible to transfer a parameter in function if it is caused like this?
color.addEventListener('change', function () {});
you are attaching event handler to color multiple times in for loop, no need for that. and the loop inside event handler is not required, you just have to change the color of clicked 'p' for that you can save the clicked p and changed its color inside change event handler of input-color, something like this:
window.onload = function () {
var color = document.getElementById('input-color');
var elems = document.querySelectorAll('.title');
var currentElem;
function chancheColor(){
for (var i = 0; i < elems.length; i++) {
elems[i].addEventListener('click', function () {
currentElem = this;
color.click();
});
}
}
color.addEventListener('change', function () {
var name = this.value;
currentElem.style.color = name;
});
chancheColor();
};
<input id="input-color" type="color">
<p class="title">First</p>
<p class="title">First</p>
<p class="title">First</p>
At each click at elems[i] you are attaching an additional change event handler to color.
Move change event attachment outside of for loop.
And generally how is it possible to transfer a parameter in function
if it is caused like this?
You can use Element.dataset to set the clicked element .dataset property to true and set siblings .dataset to false, use .querySelector() with attribute selector ".title[data-clicked=true]" to select only the element which was clicked within change handler.
window.onload = function() {
var color = document.getElementById('input-color');
var elems = document.querySelectorAll('.title');
function chancheColor() {
for (var i = 0; i < elems.length; i++) {
elems[i].addEventListener('click', function() {
for (var j = 0; j < elems.length; j++) {
elems[j].dataset.clicked = false;
}
this.dataset.clicked = true;
color.click();
});
}
}
color.addEventListener('change', function() {
var clicked = document.querySelector(".title[data-clicked=true]");
if (clicked) {
console.clear();
clicked.style.color = this.value;
}
else {
console.log("Click a .title element to change the elements' color");
}
});
chancheColor();
};
<input id="input-color" type="color">
<p class="title">First</p>
<p class="title">First</p>
<p class="title">First</p>