Variable value won't increment by 1 in Javascript - javascript

var countWrong = 0;
(Button1, Button3, Button4, Button5, Button6).addEventListener('click', () => {
countWrong += 1;
});
console.log(countWrong)
I can not figure out what I'm doing wrong. When the buttons are clicked I want to increment 1 to countWrong.

There are two problems here:
(Button1, Button3, Button4, Button5, Button6).addEventListener
You can't call a method on multiple objects like that. I assume you want something like:
[Button1, Button3, Button4, Button5, Button6].forEach(b =>
b.addEventListener('click', () => {
countWrong += 1;
})
);
EDIT: I assumed Button... where variables, but if they are IDs, you'll need to look them up first, maybe like this:
document.querySelectorAll("#Button1, #Button3, #Button4, #Button5, #Button6").forEach( ... )
Also console.log(countWrong) will always display 0, because the event handlers won't have been called yet.

You can do what you need with jquery
var countWrong = 0;
$('#Button1, #Button3, #Button4, #Button5, #Button6').on('click', () => {
countWrong += 1;
console.log(countWrong)
});
And the console.log() has to be inside your onclick function otherwise your log will only give you the initial value 0 once before any button was pressed

You have written the right code however you see the wrong results.
Because, you are printing the countWrong only once after initialising it.
So when you initialise for the first time, it's value will be zero and it prints it.
And when ou click the buttons, the value of that variable will be updated however you won't be able to see because the console.log present in outer code has already been executed ( but the value is updating ).
And your logic of binding the events for multiple ids is not as same as you wrote, change a bit.
try this :
var countWrong = 0;
['Button1', 'Button3', 'Button4', 'Button5', 'Button6'].forEach(function(e) {
e.addEventListener('click', () => {
countWrong += 1;
console.log(countWrong)
});
});

As mentioned in my comment you can't assign an event listener to multiple elements like that. The other answers have covered iterating over the buttons with forEach - here's an example with event delegation.
Add one listener to a parent container which catches events as they "bubble" up the DOM from its children (the buttons). Within the handler check that the clicked element is a button (and has a "count" class), and then increase/log the count.
const buttons = document.querySelector('.buttons');
buttons.addEventListener('click', handleClick);
let count = 0;
function handleClick(e) {
if (e.target.matches('button')) {
if (e.target.classList.contains('count')) {
console.log(++count);
}
}
}
button { border-radius: 5px; }
.count { background-color: lightgreen; }
.count:hover { cursor: pointer; }
Green buttons increase the count
<section class="buttons">
<button type="button" class="count">Button 1</button>
<button type="button">Button 2</button>
<button type="button" class="count">Button 3</button>
<button type="button">Button 4</button>
</section>

Related

How to pass arguments to the event listener's function so that there would be no duplicate event listeners?

Currently, I use the following solution:
<button onclick="initiate('ok2')" id="btn1">Initiate</button>
<button id="btn2">Send data</button>
function initiate(ok) {
document.getElementById("btn2").addEventListener("click", receiveData);
}
function receiveData(event) {
console.log(event);
}
The benefit of this approach lies in the named function receiveData, which is recognized as the same function and is not added repeatedly.
Steps to reproduce:
Press the 'Initiate' button multiple times
Press 'Send data'
Result: console log is printed only once
I want to utilize the same approach, but add an attribute to the function. I tried the bind approach, but the event listener is added multiple times. As a result, the console log is also printed multiple times.
Example:
function initiate(ok) {
document.getElementById("btn2").addEventListener("click", receiveData.bind(null, ok));
}
function receiveData(event, ok) {
console.log(event);
console.log(ok);
}
Is it possible to pass an argument to a function and not create duplicate event listeners? Ideally, it would be preferred not to delete event listeners, like in the current solution.
Here is my version with the recommended ways of delegating and setting and getting data attribute
A user cannot click what is not visible so no need to initiate the button, just unhide it
document.addEventListener("click", function(e) {
let btn = e.target
if (btn.matches("#btn1")) {
let targetBTN = document.getElementById(btn.dataset.target);
targetBTN.hidden = false;
} else if (btn.matches("#btn2")) {
console.log(btn.dataset.field);
}
});
<button id="btn1" data-target="btn2">Initiate</button>
<button id="btn2" data-field="ok2" hidden>Send data</button>
// when the window loads add a click handler to the button of choice
window.addEventListener('load', (event) => {
console.log('page is now loaded');
document.getElementById("btn2").addEventListener("click", receiveData)
});
function receiveData(event) {
console.log(event);
}
or as suggested in comments, add the click handler inline.
You need to tel it if it is inited or not..
let data = "";
let init = true;
function initiate(ok) {
data = ok
if(init ){
document.getElementById("btn2")
.addEventListener("click", receiveData);
init = false
}
}
function receiveData(event) {
console.log( data );
}
<button onclick="initiate('ok2')" id="btn1">Initiate</button>
<button id="btn2">Send data</button>
It looks like the one goal is to only allow the second button to be able to be used when the first button is clicked.
So, I attached an event listener to the document. Then used data attributes on the buttons to determine if the start button can be used or not. And just for display I used CSS to hide the start button if its not allowed to be used just yet
document.addEventListener("click",function(e){
let btn = e.target
if(btn.matches(".btn-start")){
let targetBTN = document.querySelector(`[data-id='${btn.dataset.target}']`)
targetBTN.setAttribute("data-initiated","true");
}
else if(btn.dataset.initiated == "true"){
console.log(btn.dataset.field);
}
});
[data-initiated="false"]{
display:none
}
[data-initiated="true"]{
display:inline-block
}
<button data-target="send2" class="btn-start">Initiate</button>
<button data-initiated="false" data-field="ok2" data-id="send2" class="btn-send">Send data</button>

JavaScript: binding click handler to object instance throws undefined error

Suppose I have an object oTodo that has two functions:
let oTodo = {
getTodoList: function() {
alert('test');
}
displayPageNums: function() {
for(let i = 1; i <= 3; i++) {
$('#next').before(`<li class="page-item">
<button onclick="oTodo.getTodoList()" class="page-link">${i}</button>
</li>`);
}
},
}
How can I use the getTodoList() function inside the onclick event? It throws an error saying that the function is not defined.
Any idea how can I fix this?
I also tried this which also fails:
<button onclick="${oTodo.getTodoList()}" class="page-link">${i}</button>
As you're using jQuery, consider using jQuery to bind the click event to your li elements, rather than to use inline event binding (via onclick) as you currently are.
Taking this approach would give you the ability to directly access the oTodo object and call the corresponding getTodoList() function as your click event handler like this:
let oTodo = {
getTodoList: function(event) {
const value = event.currentTarget.dataset.value;
alert(value);
},
displayPageNums: function() {
for (let i = 1; i <= 3; i++) {
/* Create list element wrapped with jquery */
const li = $(`<li class="page-item" data-value="${i}">
<button class="page-link">${i}</button>
</li>`);
/* Attach oTodo.getTodoList function as handler for click event */
li.click(oTodo.getTodoList);
/* Add li element before next */
$('#next').before(li);
}
}
}
/* Test */
oTodo.displayPageNums();
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.0.0/jquery.min.js">
</script>
<div id="next"></div>
well unfortunately you are trying to combine javascript with html it is not possible with jQuery its not jsx . usage of $ requires script tag
better will be you assign the nested obj fucn to a variable as
const someVariable = oTodo.displayPageNums;
and then you can use it with onclick as
<button onclick="someVariable()" class="page-link"></button>
For ${i} you have to bind it with Jquery and inject it with the $.html() function

event.currentTarget accumulates events on click

Dynamically created content has a delete option. I capture the event via
function requestDeleteItem() {
$(".list").on("click", ".fa-trash-alt", event => {
const itemId = $(event.currentTarget)
.parent()
.parent()
.attr("id");
const itemName = $(event.currentTarget)
.parent()
.siblings("span.name")
.text();
confirmDeleteItem(itemName, itemId);
});
}
In the confirmDeleteItem function a confirmation button is generated and the AJAX call is made via JQuery.
The delete option for each item has an icon. I can click on the icon for multiple items and then click the confirm button on the final one and it will delete all previously clicked items, rather than just one.
I want to delete just the last clicked item. I'm thinking it has to do with the current event target and that it gets saved in memory, but i am not advanced enough to more.
You need to move your $(".list").on event outside of requestDeleteItem.
What is happening is that every time you call requestDeleteItem() (to create a ajax button), you are also creating a handler for every list. Thus, your event handling code gets called over and over again when the button is clicked. Instead, when using event delegation, you only need to add the handler once, as it will pick up any buttons added later.
Here's a simplified code snippet that shows the correct behavior. Note how
$("#container").on('click' is only called once.
let num = 0;
const requestDeleteItem = () => {
$("#container").append(`<button class="del">delete ${num}</button>`);
num++;
}
$("#add").on('click', () => {
requestDeleteItem();
});
$("#container").on('click', '.del', (e) => {
console.log(`clicked on ${e.currentTarget.innerHTML}`);
})
.del {
display: block;
}
#container {
margin-top: 10px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<button id=add>add button</button>
<div id="container"></div>
For comparison, here's a BAD example that adds the event handler every time:
let num = 0;
const requestDeleteItem = () => {
$("#container").append(`<button class="del">delete ${num}</button>`);
num++;
$("#container").on('click', '.del', (e) => {
console.log(`clicked on ${e.currentTarget.innerHTML}`);
})
}
$("#add").on('click', () => {
requestDeleteItem();
});
.del {
display: block;
}
#container {
margin-top: 10px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<button id=add>add button</button>
<div id="container"></div>

Javascript - How to get multiple data attribute of button when on click

I have multiple buttons with a data-rel attribute, how do I get that attribute on each button when they are clicked?
HTML
<button class="category" data-rel="all">All</button>
<button class="category" data-rel="banana">Banana</button>
<button class="category" data-rel="orange">Orange</button>
<button class="category" data-rel="watermelon">Watermelon</button>
JS
var btn = document.querySelector('.category');
btn.addEventListener('click', function() {
var el = btn.getAttribute('data-rel');
alert(el);
})
I'm only able to get the attribute for the first button, but not the rest
var btn = document.querySelector('.category');
btn.addEventListener('click', function() {
var el = btn.getAttribute('data-rel');
alert(el);
})
.button {
padding: 10px 20px;
}
<button class="category" data-rel="all">All</button>
<button class="category" data-rel="banana">Banana</button>
<button class="category" data-rel="orange">Orange</button>
<button class="category" data-rel="watermelon">Watermelon</button>
I assume that you want a single function to handle every button. Therefore I declare the function outside of the loop, so it is declared only one time, and therefore saves memory. You are going to need this handle-function regardless of what version you use below to add the event listener.
// The handle function is declared outside the loop, saving memory.
// The current button is found in this
// Also using dataset to get the data.
function handle(event) {
alert( this.dataset.rel );
}
To get all matching elements you need to use querySelectorAll. It returns a collection, and you can use the forEach-method to easily traverse the collection:
document.querySelectorAll('.category').forEach(
function (btn) {
btn.addEventListener('click', handle );
}
);
This is the same type of call, but with modern arrow-function instead. I don't know what you prefer.
document.querySelectorAll('.category').forEach(
btn => btn.addEventListener('click', handle )
);

simulating click event of a different selector

I have a bunch of product buttons like:
<button class='prd-class' data-prd-id='1'>
Product 1
</button>
<button class='prd-class' data-prd-id='2'>
Product 2
</button>
And I have a button click function like so:
$('.prd-class').click(function(){
$('.prd-class').removeClass('cls-focus'); //remove any focused product
$(this).addClass('cls-focus'); //then focus on the selected one
$('#selected-prd-name').text($(this).data('prd-name'));
... etc
});
As you can see, it uses this object reference inside the function heavily.
Now there is another situation where at page load I want the lines inside this function to be executed.
Since there are multiple product buttons, I want to ensure that the I am simulating the click event of the required one.
$(document).ready(function()
{
$("button[data-prd-id='"+prd_id+"']").click();
});
But this does not work. How can I change the code to execute the code lines correctly?
I am not sure about your requirements. However, this demo might give you some ideas to resolve your issues.
HTML:-
<button class='prd-class' data-prd-id='1'>Product 1</button>
<button class='prd-class' data-prd-id='2'>Product 2</button>
<div>
Selected Product ID: <span id="selected-prd-name"></span>
</div>
CSS:-
.cls-focus {
border: 1px solid red;
background-color: brown;
}
JavaScript:-
(function () {
var $prdClass = $('.prd-class'),
$selectedPrdId = $('#selected-prd-name'),
prdClassClickHander = function () {
var $self = $(this);
$prdClass.removeClass('cls-focus');
$self.addClass('cls-focus');
$selectedPrdId.text($self.data('prd-id'));
},
init = function () {
$prdClass.on("click", prdClassClickHander);
};
$(document).ready(init);
}());
// Simulate the click on DOMReady
$(document).ready(function () {
var prd_id = 1;
$("button[data-prd-id='" + prd_id + "']").trigger('click');
});
JSFiddle Demo:-
http://jsfiddle.net/w3devjs/e27jQ/

Categories