jQuery different binds on containing div and button inside - javascript

So I have a button inside a list row that is used to delete the row from the page (calls ajax stuff to delete the object represented by the row, but that's not important for my question). The whole row is bound to a click event which would redirect to another page.
In other words, the containing row is click bound and the inner button is click bound, which is causing me problems since clicking the inner button also triggers the containing row click event (as it should).
I've tried binding a hover event for all delete buttons that unbinds the row click on mouseover, and rebinds it on mouseout, like this pseudocode below:
$('.delete-button').hover(
function() {
$('.list-row').unbind();
$('.delete-button').bind('click', function() { /* delete action */ });
},
function() {
$('.delete-button').unbind();
$('.list-row').bind('click', function() { /* list row action */ });
}
);
This isn't working very well, and I'm convinced there is a better way to approach it. Should I take the button out of the containing list-row? It's way easier to have it in there since my list row contains custom attributes that have data I need for the ajax calls and I can just var rid = $('.delete-button).parent().attr('row-id'); to get the data, but I'm not opposed to change :)
Thanks!

In your click event handler for the button, you need to call e.stopPropagation(). This will prevent the event from bubbling up the DOM tree. More info here: http://api.jquery.com/event.stopPropagation/
edit: you already accepted (thanks!), but maybe this code snippet would help explain some of the concepts better:
$('.list-row').click(function() {
/* list row action */
});
$('.delete-button').click(function(e) {
// die, bubbles, die
e.stopPropagation();
// if you also need to prevent the default behavior for the button itself,
// uncomment the following line:
// e.preventDefault();
// note that if you are doing both e.stopPropagation() AND e.preventDefault()
// you should just `return false;` at the end of the handler (which is jQuery-
// sugar for doing both of these at once)
/* delete action */
})

There's a few ways of approaching this. As #jmar777 has already said you may attach an altered event to the click handler on the button, stopping propagation.
If you want to do this with the same function as you're applying to the div then you can approach it as such:
if($(event.target).is("input")) {
event.stopPropagation();
}
Another approach is to actually not bind the click event to the button, for any time the browser supports clicks on the containing element. As you will always trigger that, then you don't actually need the button to handle it too! This does require you to handle IE6 etc a little differently from everything else though...

Let your handler function return false

Related

Code inside eventListener isn't called immediately?

I am trying to understand this code but it is not making any sense.
When #open_help button is clicked he is calling the handleOpen() which calls showHelp(), which calls jQuery function to show the help div, but if you see below that he is adding and removing an event listener and he also calls hideHelp(). Why is he doing that?
Is he just doing that to encapsulate hideHelp so it would wait for the button to be clicked?
// listen to "help" button
$('#open_help').bind("click",handleOpenHelp);
function handleOpenHelp(evt) {
if (!$("#help").is(":visible")) {
evt.preventDefault();
evt.stopPropagation();
showHelp();
}
}
function showHelp() {
$("#help").show();
document.addEventListener("click",function __handler__(evt){
evt.preventDefault();
evt.stopPropagation();
evt.stopImmediatePropagation();
document.removeEventListener("click",__handler__,true);
hideHelp();
},true);
}
function hideHelp() {
$("#help").hide();
}
Let's simplify the code to the bare minimum and re-arrange it a bit to see what's going on:
$("#help").show(); // Show the help dialog
// If user clicks ANYWHERE (the whole document)
document.addEventListener("click",function __handler__(evt){
hideHelp(); // Hide the help dialog
// Remove myself from the event listener so that this function
// will not be called again when user clicks anywhere:
document.removeEventListener("click",__handler__,true);
},true);
So basically it's grabbing all click events anywhere on anything (button, text, link, blank space.. literally anywhere) and execute a function to hide the help dialog. After doing that (before in the original code) it removes itself from the click event handler so that other things on the page can get clicks again.
If you'll notice hide help is basically part of "Click" event listener that is being appended onto the document element after a person clicks help. this means once the help info is shown, all you have to do is click anywhere on the page and what was shown will once again be hidden.
stoppropigation and stopImmediatePropogation are just insurance that nothing else will happen accept what he wants. It stops all eventhandlers and parent eventhandlers in their tracks.
He then removes the "Click" event listener that was added when the show event was fired. It will be added again once help is clicked.
then finally he hides the help element and waits for help to be clicked again.
Hope this answers your question.

removing a placed jquery click event listener after initial click

Got some simple functionality set up on a page. Initially I want to replace default action of a hyperlink click with some functionality which will display an overlay.
After the overlay is displayed I want to remove the event listener I have placed on the hyperlink so it reverts to what it was previously (i believe there is another event listener on here, I dont want to remove this one while removing mine). Within the overlay is another button which when clicked, should trigger the initial functionality of the button.
Ive tried the .off() jquery method, however this seems to prevent the ".mmclose" button from working.
Not quite sure where i am going wrong with this..
// placing event listener on initial link
$("#utility_0_HyperLinkLogout").click(function() {
// removing event listener(?)
$("#utility_0_HyperLinkLogout").off("click");
// preventing default button behavior
event.preventDefault();
//overlay replacing original content
I62originalContent.hide();
$("#mmi62wrapper").fadeIn("slow", function() {
// new event listener placed on button within overlay (as callback)
$(".mmclose").click(function() {
//new button should now trigger original buttons original functionality?
$("#utility_0_HyperLinkLogout").trigger("click");
})
})
});
You can use jQuery .one method to attach a handler which will be executed only once. You don't need to worry about removing this handler anymore.
Check this example:
$(".myClass").click(function() {
this.innerText += "!";
});
$("#myId").one('click', function() {
this.innerText += "?";
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<button class="myClass" id="myId">Click me twice</button>
In this example, clicking the button keeps adding "!", while "?" is only added once. Other handlers are not affected.

Creating javascript event listeners on unique rows created by php loop

Forgive me if I word the title wrong, I'm speculating on what my problem might be as I'm not a javascript coding expert. I have a series of divs that are generated by a php loop with unique ids created by adding the unique id contained in an auto increment column in the mysql db table that contains all the info for the row.
When the user clicks on the div this function fires off:
onclick=\"showModal('".$rowInfo['ID']."_row-id')\"
javascript code:
function showModal(ID) { /* code that shows hidden modal window */ }
This works fine, however now I need to start adding javascript buttons (in my case img tags with onclick functions) to the div with the showModal onclick function.
I added this code to the showModal(ID) function:
var downArrow = document.getElementById(ID+'_down-arrow'); // Down arrow is the button users click to show addition buttons/divs
downArrow.addEventListener('click',arrowCheck,false); // checks to see if down arrow was clicked, if so arrowCheck function runs and stops propagation.
arrowCheck function:
function arrowCheck(e) { e.stopPropagation(); }
This bit of code also works but ONLY AFTER the user has clicked the div once, the first time the div is clicked both functions fire off (ie the modal window and the extra buttons that the down arrow shows) but after the first click the addEventListener does it's job and clicking the down arrow only shows extra buttons, elsewhere brings up the modal, etc.
I'm guessing I need to create the event listener before the user clicks the div and fires off showModal(), is this correct? I'm not sure how to create a unique event listener for each down arrow image before the div is clicked, or even if I need to. Thanks for any help!
Since the event listener is being created within the modal function, you need to call the modal function before the other listener is even added.
To get your desired results, you could either directly add the onclick method to the image that the PHP code creates, or you could detect when the page is finished loading and add those listeners then. To do the latter, though, you'd need to either query for something the images all have in common, like a classname, or you'd have to keep track of the IDs used and manually add a listener for each. e.g.:
<body onload="initEventhandling()">
initEventHandling = function () {
var buttons = document.getElementsByClassName("image-button");
for (button in buttons) {
button.addEventListener('click', arrowCheck, false);
}
}
David Millar's code pointed me in the right direction. I tried to put the initEventHandling function in the onload of the body tag but I couldn't get it to work. It executed the function but I could not get the eventListeners to work. I solved it by creating an event listener using the onload for each img tag, so my code ended up like this:
<img id=\"".$appChart['ID']."_down-arrow\" onload=\"addEvent('".$appChart['ID']."')\" onclick=\"clickReviewArrow('".$appChart['ID']."')\" src='...' />
javascript:
function addEvent(ID) { var downArrow = document.getElementById(ID+'_down-arrow');
downArrow.addEventListener('click',arrowCheck,false); }
function arrowCheck(e) { e.stopPropagation(); }
David Millar may have been suggesting this, if so I'll mark his answer.

jquery *:not() isn't selecting everything but what's in parantheses as expected

I'm trying to append some html to a page when a div is clicked on, then when any other part of the page is clicked on (except the newly attached div and the div that was clicked that attached it) remove the attached div. For some reason my :not() selector isn't working as I'd expect it to.
$("*").on("click", "body:not(._tlh_dropdown, ._tlh_dropdown_content)", function () {
// ... remove previously attached div
});
http://jsfiddle.net/qUHAB/5/
How can I remove this div when anything is clicked on except for the two classes _tlh_dropdown and _tlh_dropdown_content?
Well:
$("*").on("click", "body:not(._tlh_dropdown, ._tlh_dropdown_content)", function () {
is almost certainly not what you want. That adds an event handler for every element on the page, asking it to check for events from the <body> to have bubbled up. It kind-of makes no sense.
And
$("body").on("click", "*:not(._tlh_dropdown, ._tlh_dropdown_content)", function () {
will have problems with your test page because there's nothing else in the <body> but your header.
Change it to the second thing above (like it was in your original question), and then add a <div> with some text in it after the content that's there, and then click in that. Alternatively, change it to:
$(document).on("click", "*:not(._tlh_dropdown, ._tlh_dropdown_content)", function () {
and that'll catch events bubbling up from the <body>.
edit — if you go with $(document), it'd also be a good idea (especially if you want it to work :-) to add an "event" parameter to the handler functions and, in each, to call
event.stopPropagation();
In the first event handler (the one that opens up the extra content), that will prevent the second handler from running when the "click" on the header bubbles up to the body. For the second handler, stopping propagation isn't as important but it'll avoid redundant attempts to close the added content.
edit some more — see the comments below; the "close" handler has some subtleties that make implementing it with the "on" selector approach kind-of problematic.

Event Handling on multiple instances of the same class - need clarification

I need some guidance in understanding why these functions are doing what they are doing...
1.) On my web page, I have three different panels that utilize a Slider function, which creates an unordered list that has slider functionality using next and previous anchor links. See the code below:
function Slider(id) {
var _this = this;
this.id = id;
if (!id) return false;
this.index = 0;
this.slider = $(this.id);
this.length = this.slider.children().length;
this.width = $(this.id).outerWidth();
this.totalWidth = this.length * this.width;
$(id).addClass('slideWrapper').wrap('<div class="slideViewport">').after('<div class="sliderNav">PreviousNext</div>').css({
'width': this.totalWidth
}).children().addClass('slide').css({
'width': this.width
});
$('.slideViewport a.next').click(function(e) {
e.preventDefault();
return _this.next();
});
$('.slideViewport a.prev').on(function(e) {
e.preventDefault();
return _this.prev();
});
}
If I try to run more than one of these Slider instances on a page, clicking a .next anchor will cause the clicked element and any of the elements below it to more to the next list element in their slideshows. Going to the second panel would cause all but the first to run, the third panel causes all but the first and second to run, etc. I would have expected the handler to only run for the event that I clicked on, rather than all instances of the class after it, since I am using this in my event. Any explanation as to what is going on here would be immensely helpful.
2.) Now, I've been trying to make it such that all of the Slider events DO run next() when I click on any a.next anchor on the page, rather than just run an event for the one whose anchor I have clicked. I have figured out that this code works:
$('.slideshow').on("click", "a.next", function(e) {
e.preventDefault();
return _this.prev();
});
But truth me told, I'm not really sure why this is working. My understanding is that JQuery is only looking to see if the a.next anchor is clicked, and will then pass the handling function to the $('.slideshow') selector, which makes me assume that it is selecting all instances of $('slideshow') and running the .next() function for all of them. Is that the right way to think about it?
3.) Why does the following snippet of code cause all of the slideshows to run the next() function twice, as opposed to once? I don't really like that this isn't returning anything, so I don't really want to use this particular bit of code, but I'd just like to understand it a little bit better...
$('.slideViewport a.next').on("click", function(e) {
e.preventDefault();
$('.slideshow').each(function() {
_this.prev();
}
});
Help understanding any of this code would be much appreciated. I would really like to have a better understanding of what is going on in the DOM in terms of propagation in this scenario, but everything I've tried to read has just made me feel more confused. Thanks!
This bit of code attaches a click handler to all .slideshow elements in the document.
$('.slideshow').click(function(e) {
e.preventDefault();
return _this.prev();
});
What you might have wanted was to attach the handler only to the .slideshow elements that are descendants of the slider:
this.slider.find('.slideshow').click(function(e) {
// ... handle event here ...
});
Now, about your on() statement:
$('.slideshow a.next').on("click", function(e) {
e.preventDefault();
$('.slideshow').each(function() {
_this.prev();
}
});
What you've done here is bind a click event handler to all the .slideshow a.next elements, and what the handler does is run _prev() on all of them. But you already have another handler bound to the .slideshow element from when you called $('.slideshow').click(). When the "on" handler is finished, the event continues to propagate up the DOM tree, triggering all of the click handlers on its way up, including the one you bound to the .slideshow element. And of course that handler also calls _prev().
If you want to stop event propagation, you have two choices.
call e.stopPropagation()
return false from your event handler (this tells jQuery to stop propagation and prevent the default action).
You may ask yourself, "what is the difference between click() and on('click', ...). The on() method lets you use Event Delegation. Basically, that means using a single event handler attached to one DOM node to handle events on a lot of descendant elements.
As an example, imagine you have a div with some arbitrary number of images in it, and you need to do something whenever an image is clicked. You can either (a) bind a click event handler to each image or (b) bind a handler to the div that will handle all the click events for all the images as those events bubble up the DOM tree.
$('#imageDiv').on('click', 'img', function(evt) {
// ... "this" is the image that was clicked ...
});
Delegation has the added benefit that you can add and remove images from the container, and the delegate will continue to work.

Categories