Onclick event not works after "show" class added to parent div - javascript

I have two screens on the page. Every screen has 5 tables, there is only one table that can be seen. I want to change the visible table with these buttons. There are 2 buttons (previous, next) for each screen.
Here is my Javascript code to buttons:
document.querySelector('.screen.show .screen-table-buttons .next').onclick = function(){
...
}
This code works when the page is loaded the first time, but when added the show class to other screens, and I want to use it, it's not working. I want to use these buttons when the screen parent has a show class.
I changed the show class, added it to other screens, reloaded the page, and then other buttons worked on the other screen, but the first buttons didn't work.
So, I just want to use all buttons when the div parent has the show class. If the parent does not have the show class, don't work, If has it, just do it.

As mentioned in the comments this is a good case for event delegation - a single listener is attached to the document (or relevant ancestor) and functions are called based on target attributes.
Here it is attached to the document and it first checks if the clicked target has an ancestor with class screen.show using closest() if it does we check the classList of the target using classList.contains() to trigger the relevant function call.
function next_fn() {
console.log('next');
}
function previous_fn() {
console.log('previous');
}
document.addEventListener('click', (event) => {
if (event.target.closest('.screen.show')){
if (event.target.classList.contains('next')) {
next_fn();
}
if (event.target.classList.contains('previous')) {
previous_fn();
}
}
});
<div class='screen'>
<h6>Screen no-show (buttons won't trigger)</h6>
<button type='button' class='next'>Next</button>
<button type='button' class='previous'>Previous</button>
</div>
<div class='screen show'>
<h6>Screen show (only these buttons work)</h6>
<button type='button' class='next'>Next</button>
<button type='button' class='previous'>Previous</button>
</div>
<div class='not-screen'>
<h6>Not-screen (buttons won't trigger)</h6>
<button type='button' class='next'>Next</button>
<button type='button' class='previous'>Previous</button>
</div>

As Teemu said in the comment, the way you're binding the click event means that you're binding the click event to the button that is already visible (i.e. the one that has the show class at the moment the line you've put in the question is hit) - it isn't designed to cater for elements that may or may not exist at some point in the page lifecycle.
You'll either need to rebind the click event each time you switch the show class, or you can bind the event regardless of the show class.
In order to rebind the event each time, you'd be better off pulling the function out into its own area - something like this:
document.querySelector('.screen.show .screen-table-buttons .next').onclick = NextClicked
function NextClicked() {
... do the normal click event stuff, and move the show class to the other screen
document.querySelector('.screen.show .screen-table-buttons .next').onclick = NextClicked
}
Alternatively, you could bind the click event once and do a check to see whether the one clicked is the right one - something along these lines:
document.querySelector('.screen .screen-table-buttons .next').onclick = function(e){
if (e.target.parentElement.parentElement.hasClass('show')){
...
}
}

Related

Trying to manipulating classes with JS to get a div element to close

I'm in the process of creating a web app that tracks real estate transactions. Part of that process is to track all the offers that come in on a specific property. An overview of the offers are displayed at first glance vertically on the page. When you click on an offer a control panel drops so that further info and/or actions can be viewed/taken. The code to drop the control panel is working and I've also got it so that if you click on another offer it closes any open control panels.
I've also included a div that I've given the class name of "close-bttn". This button is located within the main control-panel and is supposed to close the control-panel that it is a child of.
The close-bttn is supposed to close the open control panel, but it does not.
Here's a code shot of the structure
<div class="offer">
<div id="offer-amount"># dollarformat( offer_amount )#</div>
<div id="buyer-premium"># dollarFormat( buyer_premium )#</div>
<div id="buyer-name"># buyer_name #</div>
<div id="agent-name"># agent #</div>
<div id="agent-email"># agent_email #</div>
<div class="control-panel">
<div class="close-bttn">X</div>
</div>
</div>
Here is the code that I'm using to manipulate the elements
const eachOffer = document.querySelectorAll(".each-offer");
const controlPanels = document.querySelectorAll(".control-panel");
// This adds a click event to the <div class="offer"> element that opens the control-panel div
// by adding the class "scaley". The scaley class definition: .scaley{transform: scaleY(1)}
eachOffer.forEach((offer)=>{
// This. targets the specific control-panel for the offer
var o = offer.querySelector(".control-panel");
offer.addEventListener('click', ()=>{
// See function for explanation
// This closes any open control-panels
toggleControlPanel()
o.classList.toggle("scaley")
})
})
function toggleControlPanel(){
//There are multiple control-panels, one for each offer. This loops over all the control-panels
// on the page and if the classList contains "scaley" it is removed which closes the ocntrol-panel
document.querySelectorAll(".control-panel").forEach((o)=>{
if( o.classList.contains("scaley") ) o.classList.toggle("scaley")
});
}//END
Here's the code to add the click event to use the close-bttn to close the control-panel
controlPanels.forEach((panel)=>{
panel.querySelector(".close-bttn").addEventListener("click",()=>{
//This does not seem to be working.
// I've tried to use classList.toggle as well with no result
panel.classList.remove("scaley")
})
})
Not sure what to do here. Any ideas??
In addition I've used
classList.toggle
classList.remove
classList.value
//ClassList.value contains all the classes an element has
//So the control-panel has a classList.value="control-panel scaley" at onset
//I tried to do classList.value="control-panel". that didn't work.
Here is a link to a codepn example that I posted: https://codepen.io/Hugh-Rainey/pen/wvxjgpJ?editors=1111
Your remove() function is working, only problem is your click event is bubbling up and triggering the handler on your offer divs (which re-toggles them back open). You need to stop the event from bubbling up.
controlPanels.forEach((panel)=>{
panel.querySelector(".close-bttn").addEventListener("click",(e)=>{
e.stopPropagation(); // stop event bubbling, don't forget to add e to your param list
panel.classList.remove("scaley")
})
})
querySelectorAll returns a NodeListOf which is not an array. So use a for loop like this:
for (let i = 0; i < controlPanels.length; i++) {
panel.querySelector(".close-bttn").addEventListener("click",()=>{
panel.classList.remove("scaley")
})
}

Div inside of another Div avoid click

Hello my friends so this is my question i have two divs, one of them is inside of the other one
like this schema below.
<div class="button1" onclick={buttonOnePressed}>
<div class="button2" onclick={buttonTwoPressed}">
</div>
</div>
i Want to click on the div with class "button2" and call only one function that is the "buttonTwoPressed". But when i click on the div "button2" i accidentally also call the function "buttonOnePressed", so this way two functions are called but i just wish one. What can i do to prevent that ? Thank you for reading
Event bubbles up. Use .stopPropagation() to stop it from moving to parent.
let buttonTwoPressed = (event) =>{
event.stopPropagation();
}

jquery show/hide not working for dynamically created div

I have a checkbox which will allow the users to show/hide certain features. So I have 2 divs with separate IDs and one div is added directly to my code and the second div is dynamically added through a plugin with the CSS display:none.
$(function() {
$("#chkstatus").click(function() {
if ($(this).is(":checked")) {
$("#dv").show();
$("#Add").hide(); //dynamic div
} else {
$("#dv").hide();
$("#Add").show(); //dynamic div
}
});
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div id="dv" class="forgo">
<h1>Benefits</h1>
<p></p>
</div>
<!-- The below code is not visible in the page source but visible in inspect element -->
<div id="Add" class="form-group" style="display:none">
<input class="btn-block button" type="button" style="" value="Upload File">
</div>
The problem is that the show/hide is working perfectly to the #dv but nothing happens to #Add. It always stays hidden.
Can someone let me know how can make the show/hide work for the dynamically added div?
Instead of binding the event listener to the dynamically created div you should add an eventlistener on the parent (say <div id='cont'></div> ) which is ofcourse static in nature .
Say the event we are listening to is click, now whenever you click the parent you have the reference to the specific div (even if there a lot of dynamic divs like this) as a target attribute of the event. which you can find by digging the output in console log.
Why and How ??
This is because of event delegation and event bubbling i.e. the event bubbles up to the parent div and keep on bubbling up to the body in DOM tree.
So for this case, as the event is click( checkbox ) which is on some different element (no parent child relation with dynamically added div) so we can use trigger to fire our own custom event on the parent div.
$(document).ready(function () {
$("#cont").on('myEvent1',function(){
$(this).find('#dv').show();
$(this).find('#Add').hide();
});
$("#cont").on('myEvent2',function(){
console.log($(this)); // This will o/p the #cont div containing both the static and dynamic div in it.
$(this).find('#dv').hide();
$(this).find('#Add').show();
});
$("#chk").change(function () {
if ($(this).is(":checked")) {
console.log($(this)); // This will o/p the checkbox element
$("#cont").trigger('myEvent1');
}
else {
$("#cont").trigger('myEvent2');
}
});
});
You can try to mold it for your exact purpose and can also use toggle to reduce the LOC.

Why can't jquery remove these classes?

I have created a series of elements that when you click on any one of them, they will expand, pushing the other elements out of the way. Initially if you clicked on it again the element would contract, but now I want to take the close functionality and put it in a button with in the element.
Initially when you click on the element, I use jQuery to add a number of classes to a variety of elements.
$(".left > .elem").click(function () {
$(this).addClass("expand");
$(this).parent().addClass("WithLarge");
$(this).children(".bar").addClass("visible");
$(this).children(".reduce_button").addClass("visible");
$(".right").addClass("shift_right");
});
When I click the close button I would like to remove those classes.
$(".reduce_button").click(function () {
$(this).parent().removeClass('expand');
$(this).parent().removeClass("WithLarge");
$(this).parent().children(".bar").removeClass("visible");
$(this).parent().children(".reduce_button").removeClass("visible");
}
The problem is, those classes aren't budging.
You can try it out on the JSFiddle
Click on the yellow box(".left > .elem"), it expands pushing the others out of the way, click on the red box(".reduce_button"), and nothing happens. I tried
console.log($(this).parent().hasClass('expand'));
$(this).parent().removeClass('expand');
console.log($(this).parent().hasClass('expand'));
to see if it has been removed. It returns false, even though the class is still there.
Why can't I remove these classes?
You ARE removing the class. But, the click on the .reduce_button is also triggering the .elem click event, which adds the class back again.
EDIT:
As commented below by j08691, you can add stopPropagation to keep the event of going on to the next listener, like:
$(".reduce_button").click(function (e) {
$(this).parent().removeClass('expand');
e.stopPropagation();
...
Here is your fiddle updated: http://jsfiddle.net/HybHK/9/

change div class onclick on another div, and change back on body click

Let me define the problem a little bit more:
i have
<div class="contact">
<div id="form"></div>
<div id="icon"></div>
</div>
i want onclick on #icon, to change the class of .contact to .contactexpand( or just append it).
Then i want that the on body click to change the class back, but of course that shouldnt happen when clicking on the new class .contactexpand, and if possible that clicking on icon again changes the class back again.
I tried numerous examples and combinations but just couldn't get the right result and behavior.
Check this: Working example
Let's go step by step
I want onclick on #icon, to change the class of .contact to .contactexpand( or just append it). […] and if possible that clicking on icon again changes the class back again.
You want to use the toggleClass() method to achieve this. Simply:
$('#icon').on('click', function(e){
$(this).parent()
.toggleClass('contact')
.toggleClass('contactexpand');
});
Then i want that the on body click to change the class back
You will have to make sure that body removes contactexpand class and adds contact. At this point I would just give the container element an id (or class if you prefer), just to make things simpler. Then what you do is pretty simple:
$('body').on('click', function(e){
$('#thisdiv')
.removeClass('contactexpand')
.addClass('contact');
});
but of course that shouldnt happen when clicking on the new class .contactexpand.
This is the step that the other answers missed, I think. Since everywhere you click, you also click on the body element, you will always trigger the click event on the body, hence removing the contactexpand class and adding the contact one.
Enter event.stopPropagation(). This method will make sure that the events doesn't bubble up the DOM, and will not trigger the body click.
$('#thisdiv').on('click', function(e){
e.stopPropagation();
});
Working example
You can add a class to parent element like the following code.
$(".contact #icon").click(function(){
var element = $(this).parent(".contact");
element.removeClass("contact").addClass("contactexpand");
});
I like to the jQuerys toggleClass function like so:
$('#icon').click(function(){
$('#contactbox').toggleClass('contact');
$('#contactbox').toggleClass('contactexpand');
});
Or you could use addClass('className') and removerClass('className') if you would like to apend it rather than toggle it :)
Here is an example: http://jsfiddle.net/aUUkL/
You can also add an onclick event to the body of the page and use hasClass('className') to see whether or not to toggle the class when the body is clicked. You could use something like this (Although I havent tested this bit!):
$('body').click(function(){
if( $('#contactbox').hasClass('contactexpand') ){
$('#contactbox').addClass('contact');
$('#contactbox').removeClass('contactexpand');
}
});
You can do this
$('body').on('click', function(event) {
if ($(event.target).attr('id') == 'icon') {
$(event.target).parent().toggleClass('contactexpand');
} else {
$('.contact').removeClass('contactexpand');
}
});
Check out this jsfiddle
var $contact = $('.contact');
$contact.find('#icon').click(function(e, hide) {
e.stopPropagation();
$contact[hide ? 'removeClass' : 'toggleClass']('contactexpand');
});
$(document).on('click', function(e) {
if (e.srcElement === $contact[0]) return;
$contact.find('#icon').trigger('click', true);
});
http://jsfiddle.net/kZkuH/2/

Categories

Resources