Trigger event on parent originating, bubbling up from child - javascript

If I have a <div class="timely"></div> containing a table, in that table is a <th class="prev"><i>previous</i></th>...Chrome dev tools say there is an event listener on the <th>, but Firefox dev tools points out it is not on the <th>, but the parent .timely, which Chrome also points out when viewing what is in the handler for this event.
What I am trying to do is replicate what happens on click, to happen on keyup. It doesn't seem as simple as, in jQuery:
$('.timely th').each(function(){
$(this).on('keyup', function(){
$(this).trigger('click');
});
});
Because the event handler is on the .timely and it is listening from where the event bubbled up from when executing the code.
How can I replicate the click event on keyup of .timely with the context of it bubbling from the <th>?

First, note there's no reason for your each loop (unless you're doing something else in it you haven't shown). Remember that jQuery is set-based, so $("selector").on(...) sets up the handler on all elements in the set.
Re your question: Accept the event argument and use its target property as the element on which to trigger:
$('.timely th').on('keyup', function(e) {
$(e.target).trigger('click');
});
Or if you want to handle it up on .timely instead of on the th in .timely, just change the selector and use the delegating form (assuming you only want this for the th elements):
$('.timely').on('keyup', 'th', function(e) {
$(e.target).trigger('click');
});
It's tricky to get keyup on a th element, of course; the only way immediately coming to mind is to put an input element in the th so the keyup bubbles:
$('.timely th').on('keyup', function(e) {
console.log("keyup");
$(e.target).trigger('click');
});
$('.timely th').on('click', function(e) {
console.log('click');
});
<div class="timely">
<table>
<tbody>
<tr>
<th>
<input type="text">
</th>
</tr>
</tbody>
</table>
</div>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
You haven't said why you want to trigger click on keyup, but if it's just to run the click event handler, I don't recommend doing that. Instead, just use the same function for both the keyup and click handler. You can hook multiple events just by space-delimiting them in .on (.on("click keyup", ...)), or use a named function and refer to it where you hook up your click and keyup handlers.
Chrome dev tools say there is an event listener on the <th>, but Firefox dev tools points out it is not on the <th>, but the parent .timely
If you don't want to see handlers attached to ancestors, untick the Ancestors box in the Event Listeners tab:
But note that in your example code, the handler is definitely on the th, not the ancestor .timely element.

this jquery magic may not will work fast
$('.timely th').each(function(){
$(this).on('keyup', function(){
$(this).trigger('click');
});
});
you can abstract function that you want use on click and on keyup
function clickKeyupHandler (e) {
// ...
}
// somewhere when you use it use same functions to handle click and keyup
// $('.timely').on('click', clickKeyupHandler);
$('.timely').on('keyup', 'th', clickKeyupHandler);

Related

Event reverse order

I wonder how do I change the order of events.
Because I have to check when I took a focusOut with a click, if the click was inside the parent div, I can not let it happen the focusOut.
The problem, which is called first event focusOut the click event.
Is it possible to reverse the order of events? (Click - focusOut)?
http://jsfiddle.net/eL19p27L/5/
$(document).ready(function(){
$(".clean").click(function(){
$(".alert").html("");
});
$(document).on({
click: function(e){
$(".alert").append("<p>click</p>");
},
focusin: function(e){
$(".alert").append("<p>focusin</p>");
},
focusout: function(e){
$(".alert").append("<p>focusout</p>");
}
})
});
It would be even better. If I could detect what the event might turn out to generate the fucusout. Hence check if it happened within the parent div, if not a focusIn, it leaves not give a focusOut.
ATT
No, the way you have your event handler set up on a common parent, you can't change the order of the events.
But, you can attach your event handlers to the specific objects you want to watch and then you can see events for that object only.
Or, you can look at the target of the event and see which object is which and you will see that the focusOut event is on a different object than the click event.
You can see the source of the event with e.target.
Example of looking at the object that is the source of the event: http://jsfiddle.net/jfriend00/u5tLhes7/

Need to get info from any element, which was clicked, but not from parent elements

Need to get info from any element, which was clicked.
Example:
<div>text1<section>text2</section></div>
and JS
$(function(){
$('body *').click(function(){
alert($(this).get(0).tagName.toLowerCase());
});
});
If I click text2, parent element throw alert too. I need only first alert from section. How I can block next alerts from all parent elements of section.
Use event.stopPropagation() to prevent the event from firing on the containing elements.
$(function(){
$('body *').click(function(e){
e.stopPropagation();
alert($(this).get(0).tagName.toLowerCase());
});
});
Just wanted to expand on Kooilnc answer - Using on with event delegation is another option.
Event delegation would be nice if you have an event listener bound before or after on a node that needs to listen to a click handler that has bubbled up. If you stopPropagation, this obviously would be an issue.
Here's a fiddle with a demo:
http://jsfiddle.net/ahgtLjbn/
Let's say a buddy of yours has bound an event listener to a node higher up in the DOM tree. He expects any events that bubble up to it, to be handled by his script.
Using event delegation, the event still bubbles up (so your buddies code will still fire), but it will only alert once (since we called e.stopPropagation).
Calling on without event delegation, or binding the event directly using click (which, under the hood, is just calling on) will prevent the event from bubbling, so your buddies code will never run.

JQuery selector still working after I remove the class?

I have two jquery functions that work together, one depends on a class, another removes the class.
Once it is removed I would expect the functionality to stop working, but it carries on?
Whats going on?
Here is the fiddle, try it out for yourself.
<div class="container disabled">
Go to Google
</div>
<label>
<input type="checkbox" />Enable link</label>
The JS
$('.disabled > a').click(function (e) {
e.preventDefault();
e.stopPropagation();
alert('should stop working');
});
$('input[type=checkbox]').change(function () {
$('.container').removeClass('disabled');
});
It looks like you want to be using delegated event handlers rather than static event handlers. Let me explain.
When you run a line of code like this:
$('.disabled > a').click(function (e) {
this installs an event handler on any objects that match the selector at that moment in time. Those event handlers are then in place forever. They no longer look at what classes any elements have. So changing a class after you install a static event handler does not affect which elements have event handlers on them.
If you want dynanamic behavior where which elements respond to an event does depend upon what classes are present at any given moment, then you need to use delegated event handling.
With delegated event handling, you attach the event "permanently" to a parent and then the parent evaluates whether the child where the event originated matches the select each time the event fires. If the child no longer matches the select, then the event handler will not be triggered. If it does, then it will and you can add/remove a class to cause it to change behavior.
The general form of delegated event handlers are like this:
$("#staticParent").on("click", ".childSelector", fn);
You ideally want to select a parent that is as close to the child as possible, but is not dynamic itself. In your particular example, you don't show a parent other than the body object so you could use this:
$(document.body).on("click", ".disabled > a", function() {
e.preventDefault();
e.stopPropagation();
alert('should stop working');
});
This code will then respond dynamically when you add remove the disabled class. If the disabled class is present, the event handler will fire. If it is not present, the event handler will not fire.
Working demo: http://jsfiddle.net/jfriend00/pZeSA/
Other references on delegated event handling:
jQuery .live() vs .on() method for adding a click event after loading dynamic html
jQuery .on does not work but .live does
Should all jquery events be bound to $(document)?
JQuery Event Handlers - What's the "Best" method
jQuery selector doesn't update after dynamically adding new elements
Changing the class after the event handler is bound has absolutely no effect as the event handler is not suddenly unbound, it's still bound to the same element.
You have to check for the class inside the event handler
$('.container > a').click(function (e) {
if ( $(this).closest('.container').hasClass('disabled') ) {
e.preventDefault();
e.stopPropagation();
}
});
$('input[type=checkbox]').change(function () {
$('.container').toggleClass('disabled', !this.checked);
});
FIDDLE
When the selector runs, it gets a list of elements including the one in question and adds a click event handler to it.
Then you remove the class - so any subsequent jQuery selectors wouldn't get your element - but you have already attached the event so it will still fire.
The selector you have used runs on the line you declared it - it isn't lazily initialized when clicks happen.

is tr element unclickable?

When I bind a handler to a element
$('tr').on('click', handler)
And Then when I trigger the element's click event, nothing happen
$('tr').trigger('click')
instead the element within the can response to the event
$('tr td:first').trigger('click')
So does it mean that <tr> element isn't clickable?
What does your handler function do? It seems to work fine for me:
function handler(e){
alert(e.target);
}
// attach event
$('tr').on('click', handler);
// manually trigger click
$('tr').trigger('click');
Live example.
<tr>'s are DOM objects, so you should be able to attach events to them
stopPropagation() will also help.
$('tr').click(function(e){
e.stopPropagation();
});

How to prevent delegated handlers on a parent without preventing default in jQuery?

Is there any way to prevent a click from an <a> triggering delegated click handlers on its parent, while allowing the the <a>'s default behavior to occur (navigating to the href).
Here's an example that illustrates what I'm asking.
<div class="top">
<div class="middle">
link
</div>
</div>
And my JavaScript:
$(".top").delegate(".middle", "click", function(event) {
alert("failure");
});
$(".top").delegate(".link", "click", function(event) {
// ???
});
In this case, I want to be navigated to google.com when I click the link, but must NOT see the alert("failure") on my way out.
There are a few restrictions to the solution:
All event handlers must be delegated off of $(".top"), as I potentially have thousands of these in the page.
The navigation must be accomplished using browser default behavior, rather than window.location = $(this).attr("href") or similar
Using normal event binding, I could do an e.stopPropagation() in a click handler for the <a>, but that won't work due to the nature of delegation. jQuery provides another method called .stopImmediatePropagation() that describes what I want (preventing other handlers on current element, in this case the element that holds the delegated handlers), but does not actually accomplish it in this case. That might be a bug in .delegate(), I'm not sure.
Returning false from the <a>'s click handler will prevent the other handler from running, but will also do a .preventDefault(), so the browser will not navigate. Basically, I'm wondering what return false; does that e.stopImmediatePropagation(); e.preventDefault(); does not. Based on the docs, they should be equivalent.
For a live demo of the above code, here's a JSFiddle: https://jsfiddle.net/CHn8x/
event.stopImmediatePropagation() is indeed what you're after, but remember that order matters here since .delegate() listens at the same level, so you need to reverse your bindings, like this:
$(".top").delegate(".link", "click", function(event) {
event.stopImmediatePropagation();
});
$(".top").delegate(".middle", "click", function(event) {
if(!event.isPropagationStopped())
alert("failure");
});
Here's a working version of your demo with this change
The order you bound the handlers is the order they will execute, so you need that .link handler to execute and stop the propagation before the other handler runs, checking it with event.isPropagationStopped() or event.isImmediatePropagationStopped().
This normally isn't an issue at different levels, but since .delegate() is listening on the same element, it does matter.

Categories