EventListener doesn't catch buttons equally - javascript

I'm trying to build my first calculator. When adding the eventListener, the function only sometimes enters the if statement. I.e. if I click some number it doesn't log but when I try other numbers and then the first one again it works. I really don't get this behavior.
Here you can have a look (logs to the console) https://jsfiddle.net/ert54b7z/2/
const keys = document.querySelector(".keypad");
keys.addEventListener("click", function(e) {
if (e.target.matches("button")) {
const button = e.target;
const key = button.dataset.key;
console.log(key);
}
});
<div class="keypad">
<div class="clear">
<button data-key="clear" class="button"><p>C</p></button>
<button data-key="all-clear" class="button"><p>AC</p></button>
</div>
<div class="operations">
<button data-key="plus" class="button"><p>+</p></button>
<button data-key="minus" class="button"><p>-</p></button>
// ...
</div>
<div class="numbers">
<button data-key="7" class="button"><p>7</p></button>
<button data-key="8" class="button"><p>8</p></button>
// ...
</div>
<button data-key="equals" class="button equals"><p>=</p></button>
</div>

It's a good intuition to use event delegation. You check for the target of the click event, but since your buttons contain paragraph tags, they become the click target if you click somewhere in the center of the buttons. Remove the <p> tags (they aren't needed anyway), and the keys will be logged on every click.

You're attaching the event listener to the outer div. You need to attach it to the buttons.
$('.keypad').on('click', myFunction);
function myFunction(e) {
// do stuff here
}
<div class="">
<div class="clear">
<button data-key="clear" class="keypad button"><p>C</p></button>
<button data-key="all-clear" class="keypad button"><p>AC</p></button>
</div>
<div class="operations">
<button data-key="plus" class="keypad button"><p>+</p></button>
<button data-key="minus" class="keypad button"><p>-</p></button>
// ...
</div>
<div class="numbers">
<button data-key="7" class="keypad button"><p>7</p></button>
<button data-key="8" class="keypad button"><p>8</p></button>
// ...
</div>
<button data-key="equals" class="keypad button equals"><p>=</p></button>
</div>
<!-- include jquery -->
<script src="https://ajax.aspnetcdn.com/ajax/jQuery/jquery-3.4.1.min.js"></script>

Related

Associating Multiple IDs in HTML CSS to Script/Javascript

From SO threads Does ID have to be unique in the whole page? and Is multiple ids allowed in html and javascript? thread, I understand that while HTML/CSS may not allow for same ID to be linked to Javascript/Script on a webpage.
However, I am looking for an efficient & less complicated solution than simply copying over the large-sized Javascript and adding a progressive number to each id.
I have this submit button with a spinner:
<button class="submit-button" id="SubmitButton">
<div class="spinner hidden" id="spinner"></div>
<span id="buttonText">Submit Now</span>
</button>
and that is linked to a LARGE_SIZED SCRIPT as follows:
<script>
const myConst = MyNUM('<?php echo SOME_DETAILS; ?>');
// Select submit button
const subBtn = document.querySelector("#SubmitButton");
// Submit request handler
.......
.......
.......
// Several hundred lines of script code,
// including functions and other processing logic for spinners and whatnot
.......
.......
</script>
I need to have multiple such SubmitButton on the same webpage, so one way is to suffix the id with an incrementing number (like id="SubmitButton1", id="SubmitButton2" and so on)
and copy-paste the same <script></script> part for each button id.
However, that will make the webpage very bulky and lengthy.
Is there any way to use minimal code without repeating the whole block again and again, yet achieve the desired (multiple submit buttons)?
You really should delegate. If you then navigate the DOM of the target using the class names, then you have no need of IDs
document.addEventListener("click", function(e) {
const tgt = e.target.closest("button");
if (tgt.matches(".submit-button")) {
const spinner = tgt.querySelector("div.spinner");
tgt.querySelector("span.buttonText").hidden = true;
spinner.hidden = false;
console.log(spinner.textContent)
}
})
<button class="submit-button">
<div class="spinner" hidden>Spinner 1</div>
<span class="buttonText">Submit Now</span>
</button>
<button class="submit-button">
<div class="spinner" hidden>Spinner 2</div>
<span class="buttonText">Submit Now</span>
</button>
<button class="submit-button">
<div class="spinner" hidden>Spinner 3</div>
<span class="buttonText">Submit Now</span>
</button>
Maybe this helps.
// Get all elements with the same class in an array
const submitButtons = document.getElementsByClassName("submit-button") // or document.querySelectorAll(".submit-button");
// Loop through the array
for (let i = 0; i < submitButtons.length; i++) {
// Get each individual element including the element's children
const submitButton = submitButtons[i]
const spinner = submitButton.querySelector(".spinner");
const submitText = submitButton.querySelector(".buttonText");
console.log(submitButton, spinner, submitText)
}
// if you want a specific button you use the index
console.log(submitButtons[3])
<button class="submit-button">
<div class="spinner hidden"></div>
<span class="buttonText">Submit Now</span>
</button>
<button class="submit-button">
<div class="spinner hidden"></div>
<span class="buttonText">Submit Now</span>
</button>
<button class="submit-button">
<div class="spinner hidden"></div>
<span class="buttonText">Submit Now</span>
</button>
<button class="submit-button">
<div class="spinner hidden"></div>
<span class="buttonText">Submit Now</span>
</button>
<button class="submit-button">
<div class="spinner hidden"></div>
<span class="buttonText">Submit Now</span>
</button>

stop bubbling event onclick

I have the following
<p>haha</p>
<button class="btn btn-light" onclick="nextSibling.classList.toggle('d-none');">
<i class="fa fa-ellipsis-h"></i>
</button>
<div class="prev-content d-none">
<p>second reply from second account</p>
<br>
<button class="btn btn-light" onclick="nextSibling.classList.toggle('d-none');">
<i class="fa fa-ellipsis-h"></i>
</button>
<div class="prev-content d-none">
<p>reply from system</p>
</div>
</div>
I want to show only next sibling <div class="prev-content"> by click on button, but there is some strange behavior. It shows all divs or it hides all divs. I think the reason in bubbling events.
How can I resolve that?
Don't use inline JS same as you don't use inline style attributes
Use addEventListener
Use Event.currentTarget inside the function handler to refer to the event delegated Element
Use nextElementSibling
Use finally classList.toggle to toggle a specific class
const toggleNext = (ev) => {
const EL = ev.currentTarget;
const EL_next = EL.nextElementSibling;
EL_next.classList.toggle("u-none");
};
const ELs_tog = document.querySelectorAll("[data-toggle-next]");
ELs_tog.forEach(EL => EL.addEventListener("click", toggleNext));
.u-none {display: none;}
<button type="button" data-toggle-next>TOGGLE</button>
<div class="u-none">
<p>second reply from second account</p>
<button type="button" data-toggle-next>TOGGLE</button>
<div class="u-none">
<p>reply from system</p>
</div>
</div>
Additional read:
Node.nodeType

How can I loop for multiple classes in jQuery? [duplicate]

This question already has answers here:
jQuery same click event for multiple elements
(10 answers)
Closed 2 years ago.
I want to make a loop instead of adding JavaScript for each element to show the div on first click and hide the div on second click.
Only first div will be shown on clicking first div and second div will be shown on clicking on second div, I don't want to hide any div when clicking on other elements.
I want to show div on click and hide it on second click.
$(".d01").click(function() {
$(".desc1").toggle();
});
$(".d02").click(function() {
$(".desc2").toggle();
});
$(".d03").click(function() {
$(".desc3").toggle();
});
$(".four").click(function() {
$(".d04").toggle();
});
$(".d05").click(function() {
$(".desc5").toggle();
});
$(".d06").click(function() {
$(".desc6").toggle();
});
$(".d07").click(function() {
$(".desc7").toggle();
});
$(".d08").click(function() {
$(".desc8").toggle();
});
$(".d09").click(function() {
$(".desc9").toggle();
});
$(".d010").click(function() {
$(".desc10").toggle();
});
$(".d011").click(function() {
$(".desc11").toggle();
});
$(".d012").click(function() {
$(".desc12").toggle();
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<button type="button" class="d01 topic1">Safety</button>
<button type="button" class="d02 topic2">Environment</button>
<button type="button" class="d03 topic3">Climate change</button>
<button type="button" class="d04 topic4">Sustainability</button>
<button type="button" class="d05 topic5">Business strategy</button>
<button type="button" class="d06 topic6">Performance data</button>
<button type="button" class="d07 topic7">Working for You</button>
<button type="button" class="d08 topic8">Working together</button>
<button type="button" class="d09 topic9">Social performance</button>
<button type="button" class="d010 topic10">Human rights</button>
<button type="button" class="d011 topic11">Special reports</button>
<button type="button" class="d012 topic12">Key topics</button>
<div class="desc desc1">Safety</div>
<div class="desc desc2">Environment</div>
<div class="desc desc3">Climate change</div>
<div class="desc desc4">Sustainability</div>
<div class="desc desc5">Business strategy</div>
<div class="desc desc6">Performance data</div>
<div class="desc desc7">Working for You</div>
<div class="desc desc8">Working together</div>
<div class="desc desc9">Social performance</div>
<div class="desc desc10">Human rights</div>
<div class="desc desc11">Special reports</div>
<div class="desc desc12">Key topics</div>
you can use multiple selectors instead of writing one by one. for example
$('.one, .two, .three').click(function(event){
//you can use event.target to know which element clicked in case you need
console.log('clicked');
});
Since you asked for a common method, this might work
<button type="button" class="d01 topic1 btn" data-target="desc1">Safety</button>
<!-- added a common class 'btn' and data attribute 'target', the target will be your target class name -->
<button type="button" class="d02 topic2 btn" data-target="desc2">Environment</button>
$(function(){
$('.btn').click(function(event){
var tgtClass = $(event.target).data("target");
$('.'+tgtClass).toggle();
});
});

While clicking one of the three buttons cannot disable the other two jQuery

I'm developing a test. I want to disable two of the three buttons when one of them is clicked and to slideDown() a comment under the buttons when one of the options is chosen. I manage to make comments slide down but cannot disable the other comments when the other buttons are pushed. I'm using jQuery. I have already read many possible solutions but they don't seem to work in my case.
Here's a piece of my code:
<div class="btns">
<button class="btn" id="q1b1" value=0>A</button>
<button class="btn" id="q1b2" value=1>B</button>
<button class="btn" id="q1b3" value=0>C</button>
</div>
<div class="content">
<div class="com" id="q1com1">
<p>
comment1
</p>
</div>
<div class="com" id="q1com2">
<p>
comment2
</p>
</div>
<div class="com" id="q1com3">
<p>
comment3
</p>
</div>
<script>
$(document).ready(function(){
if($('#q1b1').click(function(){
$('#q1com1').slideDown(500);
$('#q1b2', '#q1b3').prop('disabled', true);
}));
else if($('#q1b2').click(function(){
$('#q1com2').slideDown(500);
$('#q1b1', '#q1b3').prop('disabled', true);
}));
else if($('#q1b3').click(function(){
$('#q1com3').slideDown(500);
$('#q1b1', '#q1b2').prop('disabled', true);
}));
});
</script>
</div>
Don't put click event handlers in an if condition. You need to assign each button it's own click handler on load and then run the code when the event happens.
To make this as simple as possible you can assign the same event handler to all the buttons then relate the button to the appropriate .com div by its index. Try this:
$(document).ready(function() {
var $comments = $('.content .com');
var $btns = $('.btn').click(function() {
$btns.not(this).prop('disabled', true);
var index = $(this).index();
$comments.eq(index).slideDown(500);
});
});
.com { display: none; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="btns">
<button class="btn">A</button>
<button class="btn">B</button>
<button class="btn">C</button>
</div>
<div class="content">
<div class="com">
<p>comment1</p>
</div>
<div class="com">
<p>comment2</p>
</div>
<div class="com">
<p>comment3</p>
</div>
</div>
Note that this approach removes the need for id attributes and is therefore infinitely extensible with no maintenance required for the JS logic.
I don't know where you got this structure, but it makes no sense:
if($('#q1b1').click(function(){
//...
}))
A click handler isn't a conditional statement. It doesn't return a boolean value. Any and every example you'll find on using click handlers in jQuery would just assign the handler:
$('#q1b1').click(function(){
//...
});
This structure assigns the function you give it as the "handler" for when that element is clicked. Which means at any point after this line of code executes, if a user clicks the #q1b1 element then this function will execute.
you need to bind click event directly inside document.ready and not inside if/else block.
User comma (,) in single string for multiple jquery selector instead of multiple string separated by comma.
See below code
$(document).ready(function(){
$('#q1b1').click(function(){
$('#q1com1').slideDown(500);
$('#q1b2,#q1b3').prop('disabled', true);
});
$('#q1b2').click(function(){
$('#q1com2').slideDown(500);
$('#q1b1,#q1b3').prop('disabled', true);
});
$('#q1b3').click(function(){
$('#q1com3').slideDown(500);
$('#q1b1,#q1b2').prop('disabled', true);
});
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script>
<div class="btns">
<button class="btn" id="q1b1" value=0>A</button>
<button class="btn" id="q1b2" value=1>B</button>
<button class="btn" id="q1b3" value=0>C</button>
</div>
<div class="content">
<div class="com" id="q1com1">
<p>
comment1
</p>
</div>
<div class="com" id="q1com2">
<p>
comment2
</p>
</div>
<div class="com" id="q1com3">
<p>
comment3
</p>
</div>
</div>
$(document).ready(function(){
$('#q1b1').click(function(){
$('#q1com1').slideDown(500);
$('.btn').attr('disabled', true);
$(this).attr('disabled', false);
})
$('#q1b2').click(function(){
$('#q1com2').slideDown(500);
$('.btn').attr('disabled', true);
$(this).attr('disabled', false);
})
$('#q1b3').click(function(){
$('#q1com3').slideDown(500);
$('.btn').attr('disabled', true);
$(this).attr('disabled', false);
});
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="btns">
<button class="btn" id="q1b1" value=0>A</button>
<button class="btn" id="q1b2" value=1>B</button>
<button class="btn" id="q1b3" value=0>C</button>
</div>
<div class="content">
<div class="com" id="q1com1">
<p>
comment1
</p>
</div>
<div class="com" id="q1com2">
<p>
comment2
</p>
</div>
<div class="com" id="q1com3">
<p>
comment3
</p>
</div>
</div>

JavaScript .closest returning null

I have the following simple layout which I am unable to change. I am trying to use JavaScript to get the extra element which is closest to the button that was pressed
With help from another question I was able to get the preventDefault part working but now I am struggling with closest
<div class="buttons">
<div class="button">
<button class="myButton">Click Me</button>
<div class="extra">64736</div>
</div>
<div class="button">
<button class="myButton">Click Me</button>
<div class="extra">5446</div>
</div>
<div class="button">
<button class="myButton">Click Me</button>
<div class="extra">78667</div>
</div>
</div>
document.querySelector('.myButton').addEventListener('click', myFunction);
function myFunction() {
event.preventDefault();
close = this.closest(".extra");
console.log(close)
}
But this is giving me null when I press the button, where am I going wrong?
A combination of closest and querySelector can be used:
document.querySelectorAll('.myButton').forEach(function(button) {
button.addEventListener('click', myFunction);
});
function myFunction(evt) {
evt.preventDefault();
var closest = evt.currentTarget.closest(".button").querySelector('.extra');
console.log(closest)
}
<div class="buttons">
<div class="button">
<button class="myButton">Click Me</button>
<div class="extra">64736</div>
</div>
<div class="button">
<button class="myButton">Click Me</button>
<div class="extra">5446</div>
</div>
<div class="button">
<button class="myButton">Click Me</button>
<div class="extra">78667</div>
</div>
</div>
More info:
https://developer.mozilla.org/en-US/docs/Web/API/Element/closest
https://developer.mozilla.org/en-US/docs/Web/API/Document/querySelector
.closest traverses up the dom and does not look at sibling or child elements. In the answer by #MaartenDev he is using .closest to step up one level and target the buttons parent div. He then chains queryselector to step back down into the children and select using the .extra class.
Another solution would be to use .nextElementSibling so you do not have to step up and back down. This would only work if your html elements were direct siblings and the item you were trying to target followed the element you were calling nextElementSibling on.
Here is an example of this.
document.querySelectorAll('.myButton').forEach(function(button){
button.addEventListener('click',myFunction);
})
function myFunction() {
event.preventDefault();
var closest = this.nextElementSibling;
console.log(closest);
}
<div class="buttons">
<div class="button">
<button class="myButton">Click Me</button>
<div class="extra">64736</div>
</div>
<div class="button">
<button class="myButton">Click Me</button>
<div class="extra">5446</div>
</div>
<div class="button">
<button class="myButton">Click Me</button>
<div class="extra">78667</div>
</div>
</div>

Categories