Javascript Event Delegation - Click Event Not Bubbling As Expected - javascript

I'm probably making a really simple error somewhere, but for some reason the event doesn't seem to be bubbling properly.
The Javascript is fairly trivial, uses jQuery at the moment for ease of DOM manipulation but don't want to rely on it for event handling.
jQuery(document).ready(function($) {
document.onclick = function(event) {
event = event || window.event;
var target = event.target || event.srcElement;
var target_class= target.className.toLowerCase();
if(target_class.indexOf('test_class') >= 0)
{
$('#log').append('<p>clicked!</p>');
event.preventDefault();
}
}
});
The HTML for the purpose of demonstration is similarly trivial.
text
<img src="http://img810.imageshack.us/img810/9111/trianglesl.png">
<div id="log">
<h3>Log</h3>
</div>
As you can see at http://jsfiddle.net/SuBQQ/1/ the hyperlink works, whereas clicking on the image doesn't trigger the correct event, and instead uses the event on the image (which I thought would then bubble up the DOM but it appears I'm wrong).
Am I doing this all wrong, or just missing something small?

The issue with your code is that clicking on the image sets the target to the img DOM object and then your code tests to see if test_class is on that object. But, the image doesn't have that class. If you set a breakpoint or an alert inside the event handler, you can see that it is working - it's just your test for the presence of the class name on the image that fails.
The event is handled by the tag and the page location is changed so there's no opportunity for the document event handler to do anything with it. If you change the href on the link to "#" so it doesn't change the page as an experiment and put some debugging in your click handler, you will see that it does bubble up to the document. But, in your jsfiddle, it doesn't work because the link is processing the event before the document and changing the page.
See this for a demo: http://jsfiddle.net/jfriend00/7a9Ha/.

Related

Listen for "event removal" in JS / jQuery

I am working with a very large application with a lot of JavaScript. I am trying to determine how I can find the location where a click event is being removed from a specific element.
There is a simple event listener on a specific field added via jQuery (this is an example).
$(document).ready(function() {
$('#id-name').on('click', function(e) {
window.alert('hello');
});
});
If break execution right after this and examine the element in Chrome Inspector, I see the event attached to the element. However, once I continue execution and the page finishes loading, the element no longer has the event. Something is removing it, but I can't find out where this is happening.
Is there a way to listen for "event removal" and trigger code then, so that I can identify where and how this is getting removed? Any other suggestions in locating where the click event is being removed?
Maybe you could override the removeEventListener method and trace it back to see what's calling it.
window.removeEventListener = (type, listener, useCapture)=>console.trace(type, listener, useCapture)

Method for triggering certain click event using .trigger() but suppressing all others

My situation is that I am trying to trigger a single event using the jQuery .trigger() method. However the element I am triggering has multiple click event listeners.
Actually finding what these listeners are and what they trigger from the source code is probably not viable as its included in the sites main JS file and its all minified and pretty much unreadable.
At the moment I know that the element when clicked performs some kind of ajax call and loads more data into the DOM of the page (which is what i want to trigger), however it also displays an overlay (which is what I want to suppress temporarily).
As its just an overlay there are workaround I can make; using a display:none on it straight after click etc. However it would be much more elegant if i could somehow suppress all click events on this element except the desired event.
Any ideas if this is actually possible? And if so how I would go about it?
You need to register your own event at the top of the event chain. And cancel the event chain in your event. Here is a solution with writing a custom jquery extention.
$.fn.bindFirst = function (which, handler) {
var $elm = $(this);
$elm.unbind(which, handler);
$elm.bind(which, handler);
var events = $._data($elm[0]).events;
var registered = events[which];
registered.unshift(registered.pop());
events[which] = registered;
}
$("#elm").bindFirst("click", function(e) {
// edit: seems like preventing event does not work
// But your event triggers first anyway.
e.preventDefault();
e.stopPropagation();
});
Reference:
https://gist.github.com/infostreams/6540654
EDIT:
https://jsfiddle.net/8nb9obc0/2/
I made a jsFiddle and it seems like event preventing does not work in this example. There might be another solution.

Dragging and dropping the img in a text editor does not fire mouseenter then

I am using Jquery text editor and while editing I want to drag and drop images on the editor. But the problem is that after dropping img on jqte the mouseenter event does not fire.
$(document).ready(function() {
$('.jqte_editor img').on('mouseenter', function() { alert("hello");
$(this).before("<div style='position:absolute'><input type='textbox'>
</input></div>"); });
});
the editor main container
<div class="jqte_editor" contenteditable="true"><img ></img></div>
Ah, I misunderstood you! Disregard my previous answer.
When you use on() on the .jqte_editor img object, you're attaching the mouseenter event to all <img> elements within the <div>, and these events are attached at the exact time that on() is called. (This is called a direct event handler.) So, when you call it while the <div> is blank, there are no <img>s to attach the event to.
That's why, when you re-open the editor that has images already in it, your on() statement actually has images to attach the event to this time.
What you need is a delegated event handler. You would use this script:
$(document).ready(function() {
$('.jqte_editor').on('mouseenter', 'img', function() {
alert("hello");
$(this).before("<div style='position:absolute'><input type='textbox'></input></div>");
});
});
Basically, you attach the event to .jqte_editor itself. Then, you see that second argument in the on()? That selector means that this becomes a delegated event handler. It will trigger on any <img> within .jqte_editor, whether that image is already present or will be added dynamically in the future.
Your code is valid, except for one issue: you're attaching the mouseenter handler to the selector .jqte_editor img. You have an img in your jqte_editor div, but since it has no src (nor does it have any CSS giving it width or height), it's an image of 0 width and 0 height. So, the mouseenter event can't trigger on that img because it isn't being rendered.
I used your code and attached the event to .jqte_editor instead, and it works as expected. Look at this fiddle to see it working.
Although, this event will fire whenever the mouse enters the div, even if the user isn't dragging anything. So, the following script detects when the content of the div is changed, and only fires if an image has been added (demo here):
var contents = $('.jqte_editor').html();
$(document).ready(function() {
$('.jqte_editor').on("mouseenter", function() {
if (contents != $(this).html()) {
var to = contents.length;
var diff = $(this).html().substr(length);
if (diff.substr(0,4).toLowerCase() == "<img") {
alert("Image added.");
$(this).before("<div style='position:absolute'><input type='textbox'></input></div>");
}
}
});
});

How to stop event propagation from within an anchor's href attribute without using onclick or onmousedown

Due to restrictions, even though it is something i avoid altogether, in a certain situation i have to use the javascript: syntax in a href attribute of an achor tag.
(EXPLANATION: In my CMS i use a rich text editor to allow the user to make changes to text elements, including links. In some cases specific javascript: calls are required and i banned onclick completely from the link editing features (to simplify the process for the user). However, as one of the links appears within a block that reacts to an onclick event, the thing double-fires)
Like this:
My problem is that this link is inside a container that already reacts to an onclick event. Therefore i wanted to pass the event object along to the doSomething() method, so that i could then use jQuery's
event.stopPropagation()
method.
Unfortunately however, it seems that passing the event object along
does not seem to work at all. Safari won't say anything while Firefox will report ReferenceError: event is not defined
I assume that this is the case because href="" is not a script-initiating attribute (such as onclick). The problem is that in this situation i won't be able to access the tag beyond what i already do.
Therefore i either need
1.) A way to pass the event object to the doSomething() function from within the href attribute
or
2.) A way to stop the event propagation right in that anchor (after its clicked) by other means.
Thank You for any constructive input!
You cannot stop event propagation from the href attribute because:
When the href code executes, it is not an event. It just executes that code, similar to the "location hack". Like entering javascript:doSomething() in the browser's address bar.
The href code executes after the events fire on the link -- including bubbling.
You can see that behavior in this jsFiddle. Note that mouseup, mousedown, and click all fire both for the link, and on the container when the link is clicked, before the href code executes.
If there are event listeners that you want to block, you'll have to find another way.
But, if you can append javascript to the document you can block the href using preventDefault().
For example:
jQuery, before version 1.7:
$("#container a").bind ("mousedown mouseup click", function (e) {
e.preventDefault();
} );
jQuery 1.7 and later:
$("#container a").on ("mousedown mouseup click", function (e) {
e.preventDefault();
} );
or (better):
$("#container").on ("mousedown mouseup click", "a", function (e) {
e.preventDefault();
} );
You can see this last version live at jsFiddle.
If you cannot alter the link itself (to use onclick) then your only option is to alter the onclick handler of the container.
Can you do something like
function containerClickHandler(e) {
e = e || event;
var el = e.target || e.srcElement;
if (el.nodeName === 'A' && someOtherMatchChecks) {
// eat event
}
else {
// process event
}
}
Well, this is an old question, but in my particular case I did find a hack around it, but it might only apply to a subset of situations. I have a div that has an onclick. But if an inside that div is clicked, I don't want that div's onclick to fire. Here is what I do:
function myOnClick () {
// loop over all <a>'s, and test if they are hovered over right now.
var allLinks = document.links;
var dont = 0;
for (var i = 0, n = allLinks.length; i < n; i++) {
// pure javascript test to see if element is hovered.
if(allLinks[i].parentElement.querySelector(":hover") === allLinks[i]) {dont = 1; }
};
if(dont)return;
// your stuff here, only fires when dont is false.
}
I learned about the queryselector trick here: https://stackoverflow.com/a/14800287/2295722
I don't know if there is a way to get the arguments if you write your javascript in href attribute. But you can get it as following in onclick, but as you say this isn't the best practice:
<a onclick="console.log(arguments)">your link</a>
in arguments array you'll get your event object.
here is a demo for you:
http://jsfiddle.net/tEw5J/1/

How attach document.click event without touching some element?

I need to click on a document to call some function, but the problem is that when I click on some element that want it doesnt react, so the code:
<body>
<div class="some_element">
some element
</div>
</body>
and js:
$(document).click(function(){
//something to happen
})
and now if I click on the div with class="some_element" the document.click event will be called, but I need to call that event ONLY when I click on the document; or it is possible the make this element an exception?
More detailed:
$('#forma').click(function(e){
e.stopPropagation();
$('#assignment_type_list').slideUp();
})
Lets say #forma - its a parent element of those element, so when I click on the page I want to slideUp someElement and:
$('#assignment_type_select, #assignment_type_label').click(function(){
$('#assignment_type_list').slideToggle();
})
this is the elements when they are clicked the other element is toggled, but the problem is that when I click on this elements the $('#forma').click - also executes, because its parent and the e.stopPropagation() - doesn't help.
All this stopPropagation stuff is right, though this'll cause your script to throw errors on older versions of a certain browser. Guess which one? a cross-browser way:
$('#elem').click(function(e)
{
e = e || window.event;//IE doesn't pass the event object as standard to the handler
//event would normally work, but if you declared some event variable in the current scope
//all falls to pieces, so this e || window.event; thing is to be preferred (IMO)
if (e.stopPropagation)//check if method exists
{
e.stopPropagation();
return;
}
e.cancelBubble = true;//for IE
});
However, you wanted to check if the element that was actually clicked, is the one you need. The problem with that is, that the way the event is passed through the DOM. In W3C browsers the event is first passed to the document, and then clambers down to the element that was actually clicked (propagates through the dom). By contrast IE dispatches its events on the element itself, and then sends it up to the document (except for the change event triggered by select elements... to add insult to injury). What this effectively means is that a click event that is registered in to body element in W3C browsers might be on its way to a checkbox of sorts, or it could be a click inside an empty div. Again, in IE, when a click event reaches the body tag, it could have been dispatched too any element on the page. So it may prove useful in your case to google: event delegation, or turn to jQuery's .delegate() method.
Or check the event object to see if the event is allowed to propagate through or not:
var target = e.target || e.srcElement;//target now holds a reference to the clicked element
The property names neatly show the difference between the bubbling model and the propagating one: in the first case (srcElement), the event is coming from a source element in the dom. In the W3C propagating model, the event is cought while it's headed for a target element somewhere in the dom. Look at it like a heat-seeking missile (w3c) versus a shower of debris after the target was shot down (IE, always the destructive one, and in this case often to late to respond to the events, and therefore to late to handle them:P)
One way to do it is to check for the event's target.
$('html').click(function(event){
if (event.target != this){
}else{
//do stuff
}
});
Here's a working fiddle
Elements on the document are part of the document, so if you click "some_element" in the document, it is obvious that event registered on document will be fired/triggered. If you dont want to execute code which was for "document" then first get the element OR "event source" which originates this event, and check if it was "some_element" in your question above.

Categories