I have a code like this:
<td>
<span class='ui-button-next'>NEXT</span>
</td>
This application contains a library and it is not allowed to be edited. On clicking "ui-button-next", they are calling event.stopImmediatePropagation() to stop something.
I want to call a function when user clicking on this "span" without touching that Library.
My custom code is like:
$(".ui-button-next").click(function(){
});
$(".ui-button-next").bind("click",function(){
});
$(".ui-button-next").on("click",function(){
});
are not working due to event.stopImmediatePropagation() on the library.
any workaround?
You can access the underlying events list (array) and insert your new click handler at the first, it will be triggered normally, however note that the order of execution should not be a problem in your case:
//this is the inner handler
$('.ui-button-next').click(function(e){
e.stopImmediatePropagation();
});
//this is the handler of your own
$('.ui-button-next').click(function(e){
alert('OK');
});
//get click handlers list
var clicks = $._data($('.ui-button-next')[0], 'events')['click'];
//get the last added handler (which is your own handler) and put it at the beginning
clicks.unshift(clicks.pop());
Demo.
UPDATE: To keep the order of execution (the default handlers are executed first, all the additional added handlers are executed after), I think you have to modify the default handlers by removing all the e.stopImmediatePropagation() methods, we have to use the eval() method here. Note that using that method in this case is totally OK.
//get click handlers list
var clicks = $._data($('.ui-button-next')[0], 'events')['click'];
//remove the e.stopImmediatePropagation() in each handler
$.each(clicks, function(i,e){
var handlerText = e.handler.toString()
.replace(/e.stopImmediatePropagation\(\)/g,'');
eval("e.handler = " + handlerText);
});
Updated demo.
Related
I have decided to remove all calls to JavaScript event from the html form elements to an external file. In doing this i registered an event for each item. However access the elements attributes using 'this' can no longer be used what I have decided to use is event.target.value for value attribute and event.target.name for name attribute. I think this is not the best implementation since I am getting some adverse results from implementing it.
Under is my implemented code and more on the issue:
JavaScript Inline Event Handler (Before)
<input type="radio" name="rdb_NewUsers" value="1" list="#{'true':'Yes','false':'No'}" onclick="configureItemsForRadioButton(this.value, this.name)"
JavaScript Registered External Event Handler (After)
var configureRadioButtons = {
onClick:function(evt){
evt.stopPropagation();
console.log(evt.target.value + '||'+evt.target.name);
configureItemsForRadioButton(evt.target.value, evt.target.name);
}
};
dojo.connect(dojo.byId("rdbNewUser"), "onclick", configureRadioButtons, "onClick");
The problem I am facing is when I click on any button it actually executes the console.log(evt.target.value + '||'+evt.target.name); even though it is not an registered event for the button. I think using event.target refers to any event executed on the page. What else can be used instead of evt.target.value to refer to the object who fired the event's value and name.
If you're going to avoid using a DOM manipulation tool like jQuery (which I do not recommend) you can do the following:
var myButton = document.getElementById("myButton");
myButton.addEventListener('click', function(){alert('Hello world');}, false);
If you use jQuery (recommended) it would work like so:
$('#myButton').on('click', function() {alert('Hello world');});
I'm not sure what dojo.connect method does, but this is how you normally attach an event to an element on the page. From your code, it's hard to understand how exactly you are bind your events with the function.
var configureRadioButtons = {
onClick: function(e) {
e.stopPropagation();
console.log(e)
}
};
document.getElementById("someID").addEventListener('click', configureRadioButtons.onClick, false);
<button id="someID">Click me</button>
After reviewing my code i soon realized i had overlooked something very important. My radio buttons actually carry a different id at run time rdb_NewUserstrue and rdb_NewUsersfalse and i was not registering events for these elements hence the reason the event kept firing on any click event, it was not finding an element with the id rdb_NewUsers. My solution is under:
I query the DOM for all radio buttons and got their Ids and based on the event/radio button clicked i attached a function (all radio buttons are handled by one function). In this way i did not need to hardcode an id for the radio button.
var configureRadioButtons = {
onClick:function(evt){
evt.stopPropagation();
//console.log(evt.target.value + '||'+evt.target.name+ '||'+evt.keyCode);
configureItemsForRadioButton(evt.target.value, evt.target.name);
}
};
var radios = document.querySelectorAll('input[type=radio]');
for (var i = 0 ; i < radios.length ; i++){
//console.log(radios[i].id);
dojo.connect(document.getElementById(radios[i].id), "onclick",configureRadioButtons, "onClick");
}
I am cloning a div tag when user click on available div. After cloning it I am inserting it after clicked div tag. This new div tag also has cross link to delete it. However when I click on cross link it does delete that particular div. It means it is working. Now I also have one function named foo. This method is called upon adding and deleting div tag. But when I click on delete link, this foo function is called twice. I know its reason because I am attaching click event to both parent of delete link and to delete link itself therefore, it is called twice. But How can I make it call once upon both adding and deleting div element?
Here my JSFIDDLE
Html:
<div class="container">
<div class="bx"></div>
</div>
Here is js
$('body').on('click', '.bx', function (e) {
var clone = $(this).clone();
clone.append('<a class="bx-del" href="#">X</a>');
$(this).after(clone);
foo();
});
$('body').on('click', '.bx-del', function (e) {
e.preventDefault();
$(this).parent().remove();
foo();
});
function foo() {
console.log('foo');
}
My original answer was :
Assuming .bx-delis inside .bx(better to add html as well in the question) then adding e.stopPropagation() to the .bx-delcallback should work.
E.g.
$('body').on('click', '.bx-del', function (e) {
e.preventDefault();
e.stopPropagation();
$(this).parent().remove();
foo();
});
Documention here
It has been pointed out that both events are actually attached to the body and run as delegated events. This solution seems to work but the question is whether or not it is reliable. The question here is in what order are delegated events run? Are they always run "depth first" or based on the order they were added or what? Running them depth first would be the most logical in my view, but performance considerations will play a large role here too.
I have been unable to find any hard documentation on this. The jQuery .on() documentation says
jQuery bubbles the event from the event target up to the element where
the handler is attached (i.e., innermost to outermost element) and
runs the handler for any elements along that path matching the
selector.
which could be interpreted as saying that, but could also be an explation of the concept of delegation.
Thus a safer solution to the the original problem, in my view, would be to combine the two lick events into one:
$('body').on('click', '.bx', function (e) {
var t = $(e.target);
if( t.hasClass('bx-del') || t.closest('.bx-del').length > 0){
e.preventDefault();
t.closest('.bx').remove();
foo();
} else {
var clone = $(this).clone();
if( $('.bx-del', clone).length == 0 ){
clone.append('<a class="bx-del" href="#">X</a>');
}
$(this).after(clone);
foo();
}
});
(I have also fixed so that cloning a div with a delete button doesn't add a second delete button).
This is what event.stopPropagation() is for (docs). It prevents the event from bubbling up into the DOM tree. If you add e.stopPropagation() to your delete link click handler, it will not bubble up to the div that contains it and trigger the click handler there too:
As pointed out in the comments, this is not correct, even though it appears to work correctly. Both event handlers are attached to body, and only via a 'trick' run on specific elements. The event is already at the body element, and will therefore not bubble any further. Furthermore, event.stopPropagation() should not stop other handlers from execution, event though they seem to do that right now anyway. The correct function to use here is event.stopImmediatePropagation() (docs at jquery).
$('body').on('click', '.bx-del', function (e) {
e.preventDefault();
e.stopImmediatePropagation();
$(this).parent().remove();
foo();
});
Because you cannot stopPropagation to the <body> tag when the event has already propagated to the <body> tag and both your events are on the <body> tag, I would suggest just attaching the delete event handler right to the delete X object and not use propagation for that event handler. You can then stopPropagation of that click so it won't be seen by the parent at all. In your case this is fairly easy.
Working demo: http://jsfiddle.net/jfriend00/7CDNG/
$('body').on('click', '.bx', function (e) {
var clone = $(this).clone();
clone.append('<a class="bx-del" href="#">X</a>');
$(this).after(clone);
clone.find('.bx-del').on('click', function(e) {
$(this).parent().remove();
foo();
// stop propagation and prevent default
return false;
})
foo();
});
function foo() {
console.log('foo');
}
Alternatively, you could put the .bx handler on the document object and the .bx-del handler on the body object and then you could stopPropagation up to the document object from the .bx-del handler. But, it seems in this case, it is much cleaner to just attach the delete event directly to the delete object and not use delegated event handling to avoid any possible misinterpretation of the click event.
Apparently the other answers are getting away with using .stopPropagation() on one body event handler to stop the other one from firing. Here's a quote from the jQuery documentation for .stopPropagation(): "I think you're getting lucky that's all. This is taken directly from the jQuery documentation for stopPropagation(): "Note that this will not prevent other handlers on the same element from running." Since both of these event handlers are on the body object, it does not seem like a wise and safe move to use .stopPropagation() from one to stop the other from running. It's my opinion that that technique is not safe and is not the conceptually correct way of solving this problem.
I have several places throughout my code where I use .on to attach events (usually to delegate the events). We're changing around how we're doing a few things, and we're now wanting to add a .disabled class to the elements that we want to be disabled. I'd like to block all the events on disabled items without having to refactor each location, I'm wondering if it's possible.
Example code: I've added this to the top of my script
$('body').on('click', '.disabled', function(event){
console.log("blocked");
event.stopImmediatePropagation();
// event.preventDefault();
// event.stopPropogation();
return false;
});
And an example of my normal events:
$('.ActionsContainer').on('click', '.Link', functions.ClickAction);
Problem is that even with the return false and all the others it still runs both the "blocked" and functions.ClickAction
Is there anyway around refactoring every one? I mean I can change that line below to:
$('.ActionsContainer').on('click', '.Link:not(.disabled)', functions.ClickAction);
but that's really annoying, and feels brittle.
It's not too hard. You'll need to take advantage of jQuery's special events and basically override calls to any of the original event handlers setup in the existing code. jQuery's special events hooks let you override a number of features of the event system. jQuery essentially sets up it's own handler on an element the first time a listener is attached, and then adds the callback for the listener to its queue. As other listeners get attached to the element later, their callbacks get added to this queue as well.
Using the 'events.special.click' hook, we can add a function that gets called prior to any callbacks on that element's event queue which lets us intercept the call and check for, as you mentioned, that the element has a 'disabled' class and if so, stop the original callback from executing; or if it doesn't have the class, allow the original callback to execute normally.
I've put together a jsFiddle to show how it works. See if that solves your issue. The code for the override using special events is embedded below the link:
http://jsfiddle.net/datchley/bthcv/
// ADDED TO OVERRIDE CLICKS ON 'DISABLED' ELEMENTS
(function($) {
$.event.special.click = {
add: function(handle) {
// Save original handler
var orig_handlefn = handle.handler,
$el = $(this);
// Reassign our new handler to intercept here
handle.handler = function(ev) {
if ($el.hasClass('disabled')) {
// Don't allow clicks on disabled elements
$('.output').html('<b>Warning</b> You clicked a disabled element!');
ev.preventDefault();
}
else {
return orig_handlefn.apply(this, arguments);
}
};
}
};
})(jQuery);
Assuming every .Link has that container and you're handling all events at that container, this is the most straightforward way:
$('.disabled').click( function(e){ e.stopPropagation(); } );
stopProp prevents that event from ever bubbling up to the action containers.
So i have some data on a page (a table) which based on some options elsewhere may get ajax reloaded from the server.
This table has buttons in it that can be clicked to make other things happen to the records in the table.
I notice that this ...
http://api.jquery.com/on/
... is the recommended approach for attaching simple event handlers to elements but that only attaches to elements that exist right now, and when I do my ajax load I lose the attached handlers.
So I started using this ... http://api.jquery.com/live/ ... and guess what, jquery team did their usual and deprecated it saying I should be using "on".
These functions behave very differently yet jquery docs say i should be using them interchangably so ...
Can someone explain the "on" equivelent of this and how I can get it to work with elements after an ajax call replacing the elements that hae previously been attached to ...
$("some selector").live('click', function (e) {
// some code code
e.preventDefault();
return false;
});
My understanding is that you would do something like ...
$("some selector").on('click', function (e) {
// some code code
e.preventDefault();
return false;
});
My guess is that I then have to re-run this code after performing my ajax call by putting this in to some sort of "initClicks" function and calling it both on page load and after the ajax call.
This seems to be a bit of a back step to me ... or have i missed something here?
Since the elements are added dynamically, you need to use event delegation to register the event handler
// New way (jQuery 1.7+) - .on(events, selector, handler)
$(document).on('click', 'some selector', function(event) {
// some code code
e.preventDefault();
return false;
});
Also, either use e.preventDefault() or return false, as:
return false = e.preventDefault() + e.stopPropagation()
So, there is no need to use both of them at same time.
When you use .on('click', function (e) {}) function, it works only for existing elements.
To handle click event on all selector elements, even for elements which will be added in future, you can use one of these functions:
$(document).on('click', "some selector", function (e) {
// some code code
e.preventDefault();
return false;
});
or:
$("body").delegate("a", "click", function () {
// your code goes here
});
For more information read article about Understanding Event Delegation
live() is not magic, it cannot see future elements, what it was doing is to attach a listener to the first root element of your page document and checks every bubbled event if it match your target selector, and when it find a match, it executes your function.
this is called event delegation
live() has been deprecated for good reasons, mainly the performance hit caused by using it.
then the jQUery team introduced the delegate() function which gave us a new way to achieve the exact result, but it has addressed the performance hit very cleverly by limiting the scope in which it will listen to bubbled events to the possible nearest parent of your now & future elements.
when they introduced the On() function, they gave you the ability to use it as normal event handler, or as a delegated handler for future elements.
so I believe they did a good job for this, giving us the flexibility to use it as we wish according to the specific scenario.
Code Examples:
using delegate():
$( "#TAGERT_ID" ).delegate( "a", "click", function() { // your code goes here}
using on() (for delegated events)
$( "#TAGERT_ID" ).on( "click", "a", function() { // your code goes here}
both ways are the same, and will handle future clicks on a which will be added in the future inside your TARGET_ID element.
TARGET_ID is an example for using ID for your selector, but you can use whatever selector according to your specific need.
The equivalent of said live is
$(document).on('click', "some selector", function (e) {
// some code code
e.preventDefault();
return false;
});
The on() is a single stop for all event handler formats, the model you used is the same as
$("some selector").click(function (e) {
// some code code
e.preventDefault();
return false;
});
which does work based event delegation.
You can never actually attach event listener to an element which does not exist in DOM yet. What live and on method do is attach listener on a parent which exists right now. live is nothing but an on attached on document itself.
I need some help with the callbacks. For some reason, they don't work really well.
I'm making a game with jQuery. I have a <div id='button'></div> for all the buttons that are going to be in the game. The game is going to have two buttons that make actions, and a question on top of it. The question is controlled by a <h3 id='text'></h3>. What I want to know, is that for some reason I can't set callback functions to the button's ID's. In example,
I'd have the yes or no, that have their own id's set through jQuery like this:
$('#button').html('<button id='yes'>Yes</button><button id='no'></button>');
But for some reason, I would be able to set this:
$('yes').click(function(){
//function I would want
});
Of course, that's not what my code has, that was just an example. Here's the real code:
$(document).ready(function(){
$('#main,#batman,#car,#cop,#hobo,#knife,#gangfight,#ganggun,#gangknife,#blood,#hr').hide(-100);
var hr=$('#hr');
var main=$('#main');
var batman=$('#batman');
var car=$('#car');
var hobo=$('#hobo');
var cop=$('#cop');
var knife=$('#knife');
var gangfight=$('#gangfight');
var ganggun=$('#ganggun');
var gangknife=$('#gangknife');
var blood=$('#blood');
var text=$('#text');
var button=$('#button');
$('#start').html('Are you ready to play?');
$('#button').html('<button id="yes">Yes</button><button id="no">No</button>');
$('#yes').click(function(){
$('#yes,#no').hide(function(){
$('#start').hide();
main.fadeIn(-100);
hr.fadeIn(-100,function(){
text.delay(1000).html("You were just wandering around in the streets of new york, when suddenly.. You see batman!! You've never really liked him, what do you do?")
button.html('<button id="fight">Fight</button><button id="leave">Leave</button>',function(){
batman.fadeIn(1000);
$('fight').click(function(){
});
$('leave').click(function(){
text.fadeOut(function(){
text.text('Good call. As you leave, you encounter a hobo. What do you do?');
});
});
});
});
});
});
$('#no').click(function(){
$('#yes,#no').hide();
$('#start').text('Oh, okay then. Come back later!');
});
});
I'm just wondering.. How can I set callback functions to the 'fight' and 'leave'.
If you're wondering why there's all these variables at the start, those are just the images and characters.
You can't set a click handler on an element that doesn't exist. What you should do is use .on to bind a element further up the tree. Something like:
$("#someparentelement").on("click", "#yes", function() {
// your code
});
Which version of jQuery are you using? You should probably use jQuery.on() in this situation since your click handler code probably gets executed before the button is actually available in the DOM.
$("#button").on("click", "#yes", function (event) {
// Your yes-button logic comes here.
});
For more details and possibilities, read about the .on(events [, selector ] [, data ], handler(eventObject)) method in the jQuery documentation:
If selector is omitted or is null, the event handler is referred to as direct or directly-bound. The handler is called every time an event occurs on the selected elements, whether it occurs directly on the element or bubbles from a descendant (inner) element.
When a selector is provided, the event handler is referred to as delegated. The handler is not called when the event occurs directly on the bound element, but only for descendants (inner elements) that match the selector. jQuery bubbles the event from the event target up to the element where the handler is attached (i.e., innermost to outermost element) and runs the handler for any elements along that path matching the selector.
In this case, you want to delegate the event since your element is not yet available in the DOM when you're binding the event.
Don't use the click(), use on('click') and attach it to the document.
Creating a handler this way, will ensure that any new elements will be able to trigger the event.
$('fight') selects fight tag, not the tag with fight id. Try to use $('#fight') instead.