With jQuery, is it possible to add elements to a previously defined event handler? I realize the right way to do this would be to define the handler using on() so that dynamically inserted elements will be included, but the issue is the handler I'm trying to extend is defined by code on a server that I don't have access to. The server outputs code like this at the top of the file:
$('#wrapper .block h2').click(function() {
//do stuff
});
Then, towards the bottom of the page, I've added some custom javascript code that inserts another #wrapper .block h2 element. I'm wondering if there is any way for me to include this new element in the event handler that was defined by the code at the top of the page where the click handler was registered.
Or, is there any way for me to programmatically access the callback function that was defined for that event handler? That way I could recreate it myself using the on() method.
To achieve this you need to access internal event handles stored by jQuery. In previous versions (prior to 1.8) you could do it using Data API, like $('.some').data('events'). However it's no longer available. However you can still access element data object with $._data method. Then you would do something like this:
var events = $._data($(selector)[0], 'events'),
click = events && events.click;
if (click) $el.on('click', click[0].handler);
However be aware that $._data is a private property, and there is no guarantee that it will not change one day (like previously you could access element data with $.cache, but it's gone now).
Demo: http://jsfiddle.net/dfsq/e94hau62/
It should be possible for you just to write a new event handler on the same action, it should replace the old one and leave the action to your new one.
Try it out in the console or commandline.
The simplest way is, you define the handle in server side, it will be like,
var yourhandler = function() {
//do stuff
});
Then after you load your new elements, you can hood the "yourhandler" to them, like,
$( document ).ready(function() {
$('#wrapper .block h2').click(yourhandler);
});
Related
I have this bit of code that monitors clicks on <div class="selectable_item">
$(function(){
$("#matchres .selectable_item").on("click", function(){
console.log('Sending request')
$.post("/request", $.param({'crit_id': this.id}), function(){}).fail(function(){console.log("matchres error...");});
return true;});
});
What I'm noticing is when I use the chrome console, for example, to see if there are any $("#matchres .selectable_item"); it finds them, and if I define in the console $("#matchres .selectable_item").on("click", function(){console.log('hi')}); the action is as expected and the console logs correctly. But what I showed you above does not work. Any ideas why that is? Any help would be very much appreciated. As added information, I'm using jquery v1.10.2.
#Hanlet's idea is correct, at the time of document load those items don't exist because you're dynamically creating them, and they do exist by the time you interact with them in the developer console. What you want to do is bind the event handler to a delegate, or an object that will listen for events on child elements.
What you do not want to do is add delegate callbacks to the document when avoidable. Any click on the document will have to check against its event target to see if it should trigger this document delegate callback. You do this enough times and it becomes a performance concern. Instead, pick the closest ancestor element that isn't dynamically created.
For instance, if you're creating .selectable_item dynamically but not #matchres, then add this:
$('#matchres').on('click', '.selectable_item', function () { ... });
Because you add these dynamically, this could be an event delegation issue, very common. Try this instead:
$(document).on("click", "#matchres .selectable_item", function(){ ... }
The problem consists mainly in the fact that you bind these when the DOM is first built, and then you add more elements dynamically, but the event is not bound to these new elements.
Look at these two examples:
http://jsfiddle.net/hescano/aKfWf/
and
http://jsfiddle.net/hescano/aKfWf/1/
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.
I'm trying to use 'click' on a dynamically generated DOM. I know that I can just use live or on, however my dynamically generated content is within multiple dynamically generated pieces of content and live/on no longer works.
So my code looks something like this but with more elements before El_b:
El_a = document.createElement("li");
El_b = document.createElement("a");
El_b.id = "myEl";
El_a.appendChild(El_b);
Is there a way to make this work?
PS: I've also tried the livequery jQuery plugin.
As far as delegation is concerned, you always have at least one static DOM element available to you, which is the document. If you can't find a closer element to delegate to, delegate to this.
However, delegation seems to be unnecessary here. The entire process of creating your elements and attaching listeners could be condensed to:
var a = $("<li/>").append($("<a/>").attr("id", "myElement")).click(function () {
alert('hello');
});
If, as you say, you cannot change the object creation, you can still select it by its ID and attach the listener:
$('#myElement').click(function () {
alert('hello');
});
Also, those are document fragments, not documents proper, and certainly not DOMs.
If you want to bind a function on the click event on El_b, you can just do this :
$(El_b).click(function() {
// Your code here
});
But you can use on i think. Even if you create multiple DOM elements. You can use the document or the body. Example :
$('body').on('click', 'li a.my_class', function() {
// Your code here
});
I am using Jquery to dynamically add some HTML into a page.
Now this new HTML code should trigger additional Jquery functions to enable more processing to be done but this new HTML code isnt recognized and thus the additional Jquery functions arent triggered.
How can I get the new HTMl code to be recognized and the additional functions triggered?
Thanx
It depends on what you want to do. The first thing to look into would be jQuery's .live() methods. You can associate events to matching elements that either exist or will exist in the future. For example, this click method will only bind to existing elements with the class of 'clickme'
$('.clickme').bind('click', function() {
// Bound handler called.
});
However, if you bind it using the.live() methods then it will work for existing elements and any new elements that are created:
$('.clickme').live('click', function() {
// Live handler called.
});
These examples are taken right off the API page for the live method. Check it out here: http://api.jquery.com/live/
There are 2 concerns normally, event handlers and plugins, which are two different things.
Part 1: Event Handlers
Event handlers are easy, because they act upon events, events behave identically no matter when the element as added. For this there's .live() and .delegate(), .live() listens for events on document and runs if an event comes from an element that matches the selector, let's take a table row for example:
$("tr").click(function() { ... });
This would find all current table rows, when it ran and bind a click event handler to them, the same as .bind('click', function). Then there's .live(), like this:
$("tr").live('click', function() { ... });
This listens for the click event to bubble up to document (this happens automatically, by default) and executes the handler...current and future elements behave the same way here. This means it works for both. Then there's .delegate() which is a local version of .live() like this:
$("#myTable").delegate('tr', 'click', function() { ... });
If you're just adding rows to #myTable but not removing/adding the table itself, the same type of listener for bubbling events can sit there, instead of all the way up on document, this means the event has to bubble fewer times before reaching the handler you want to execute.
Part 2: Plugins
Plugins are a bit trickier, because they take elements and do things with them (this is true for most plugins). You have two decent options here, either running the plugin when new elements yourself, for example loading via $.ajax() or a shorthand version would look like this:
$.ajax({
//options...
success: function(data) {
//add elements
$("tr", data).myPlugin();
}
});
This finds new <tr> elements, but only in a local context (in the returned HTML) and executes only on those elements. Alternatively, there's a plugin for this, less efficient, but usually not a noticeable difference on most pages. The .livequery() plugin actively looks for and acts up new elements, the same code would look like this:
$("tr").livequery(function() {
$(this).myPlugin();
});
Either of these are valid solutions, just see which fits your needs better.
More details might be helpful but it sounds like Jquery.live() might be what you need. Jquery.live() binds handlers to elements dynamically.
I have some code in which I am creating an dijit.Dialog with an Ajax call(href property) to populate the contents.
I what to parse the returned content to dojo.connect a click event to the links in the returned content.
But when I do a dojo.query("a",this.current_popup.domNode) I get an empty array back
Does anybody know how can get can attach a onclick to all the returned content?
Paul
It is less expensive to just bind an event handler to the DOM node of the Dialog widget, and use event delegation to capture any "a" clicks. This way, you can avoid any event handler cleanup, particularly if the contents of the Dialog change frequently. You can avoid the event handler cleanup if you use the widget's connect method to do the work.
So, if you are doing the connect inside a method in the dijit.Dialog, you could use something like:
this.connect("onclick", function(evt){
var node = evt.target;
if("a" == node.nodeName.toLowerCase()){
//node is an a tag, do what you want with it,
//for example, read node.href to get the URL attached to it.
//If you want to prevent following that URL and prevent further
//event bubbling, stop the event:
dojo.stopEvent(evt);
}
});
If you are doing this connection work outside the widget instance, instead of using this.connect, use widgetInstance.connect(), assuming widgetInstance is a variable that refers to the dijit.Dialog instance.
By using that version of connect, the widget will automatically unregister the event handler when the Dialog widget is destroyed, keeping the memory profile in check.
One way I found to do this was to add a delayed connect to the widget
dojo.connect(this.current_popup, "onDownloadEnd", function(){
dojo.query("a.popup",this.current_popup).forEach(function(node) {
// add your function add here
});
});
This runs after the ajax call is complete so you can now find the objects in the DOM