stop bubbling event onclick - javascript

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

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>

How to open offcanvas from offcanvas

I have a little problem with open one offcanvas from previous canvas. Generally I have canvas like this using bootstrap 5:
<button class="btn btn-primary" type="button" data-bs-toggle="offcanvas" data-bs-target="#offcanvasBottom" aria-controls="offcanvasBottom">Toggle bottom offcanvas</button>
<div class="offcanvas offcanvas-bottom" tabindex="-1" id="offcanvasBottom" aria-labelledby="offcanvasBottomLabel">
<div class="offcanvas-header">
<h5 class="offcanvas-title" id="offcanvasBottomLabel">Offcanvas bottom</h5>
<button type="button" class="btn-close text-reset" data-bs-dismiss="offcanvas" aria-label="Close"></button>
</div>
<div class="offcanvas-body small">
<button class="btn btn-primary" type="button" data-bs-toggle="offcanvas" data-bs-target="#secondoffcanvas" aria-controls="offcanvasBottom" data-bs-dismiss="offcanvas">Open second offcanvas</button>
</div>
</div>
second offcanvas:
<div class="offcanvas offcanvas-bottom" tabindex="-1" id="secondoffcanvas" aria-labelledby="offcanvasBottomLabel">
<div class="offcanvas-header">
<h5 class="offcanvas-title" id="offcanvasBottomLabel">Offcanvas bottom</h5>
<button type="button" class="btn-close text-reset" data-bs-dismiss="offcanvas" aria-label="Close"></button>
</div>
<div class="offcanvas-body small">
second content
</div>
</div>
My scenario:
Click to open first offcanvas
Click on the button in canvas
Resulst:
first canvas hide
second canvas open
Read "how it works"...
"Similar to modals, only one offcanvas can be shown at a time."
However, you can open one from the other. Programmatically show the 2nd when the 1st is hidden...
var offcanvasBottom = document.getElementById('offcanvasBottom')
var secondoffcanvas = document.getElementById('secondoffcanvas')
offcanvasBottom.addEventListener('hidden.bs.offcanvas', function () {
var bsOffcanvas2 = new bootstrap.Offcanvas(secondoffcanvas)
bsOffcanvas2.show()
})
Demo
Thanks You #Zim. This resolve my issue. I have one question jet. What You think about identify if event was triggered from button on the offcanvas and no triggered from other place? Because now if You click in othe place second canvas is open too
try this
function toggle() {
document.getElementById('offcanvas').classList.toggle('show');
}

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();
});
});

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>

EventListener doesn't catch buttons equally

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>

Categories