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.
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'm using a third-party commenting plugin, and I would like to change the content of some of the buttons. This is straightforward for buttons with id's known ahead of time, but it also has buttons that don't appear until a 'Reply' button is clicked. To be clear, these elements are not present when the page is loaded. They are inserted into the DOM following some event. For those elements, I only know a prefix of the id.
My first thought was to use .on, and to delegate to the children of the reply container, but the load event does not bubble, so this doesn't work:
<script>
$("#container").on("load", 'a[id|="reply-button"]', function(event) { $(this).html("different text"); } );
</script>
<div id="container">
<a id="reply-button-42das56ds6d78a">some text</a>
</div>
What's the next best thing?
"I know they will appear when the 'Reply' button is clicked. When that happens, new elements are inserted into the DOM, and I know what the prefix of the id of those elements will be."
You could use something like the DOMSubtreeModified event to tell when elements are added, but that isn't supported by all browsers. (In fact it has been deprecated.)
Or you could attach a click handler to the 'Reply' button:
$(document).ready(function() {
// initialise plugin here, then:
$("some selector for the reply button(s)").click(function(e) {
// setTimeout(function() {
$('a[id|="reply-button"]').html("different text");
// }, 10);
});
});
jQuery ensures that multiple event handlers will run in the order they are bound, but of course this only applies to handlers added with jQuery. So if the third-party commenting plugin you are using also uses jQuery then just be sure it is initialised first and your own reply click handler should run afterwards and at that time it will be able to access the elements added by the plugin.
If the plugin doesn't use jQuery you can't be sure your click handler will run last so instead uncomment the setTimeout code I've shown above - it will wait a few milliseconds to give the plugin events time to run and then update the text.
Use the selector $('#^=id')
id being the prefix
e.g all ids starting test123
$('#^=test123')
this would work for things like
test1234
test12345
test123fgjfdgj
This might help: http://oscarotero.com/jquery/
And use jquery event listeners for the page load..
e.g. $(document).ready(function(){});
If they are loaded when a button is clicked then do..
$('#buttonid').click(function() {//handle click});
You're looking for DOM Mutation Events. This spec allows you to be notified when DOM nodes are inserted, changed, etc. Browser support is not really there, though... well, IE is behind (IE >= 9 has support). It's also a major performance hog. See this MDN document. For these reasons, I don't think a lot of folks here would suggest using them. Here's some code, though:
document.addEventListener("DOMNodeInserted", function(e) {
if ($(e.target).is('selector matching new elements')) {
//do what you want with e.target, which is the newly-inserted element
}
}, false);
There is a performance-boosting hack involving listening for CSS animation events instead: here. Only problem is that IE9 does not support CSS animations. I think we're all waiting for the day when we can use these events in a cross-browser and performant way, though.
I hope I'm making the question clear.
Say you have some check-boxes (or any other type of element for that matter) for which you register events handler when the page loads. Then you add some more check-boxes using AJAX (so no page reload). But you also want these newly added check-boxes (after the page was loaded) to have the same registered events handlers?
What I tried was this but I feel there has to be a better approach:
$(document).ready(function () {
// Register custom envets handler
registerCustomEventHandlers();
$('a').on('click', addExtraFields);
});
function registerCustomEventHandlers() {
$('input.class_name').on("change", sayHelloWorld);
}
function sayHelloWorld() {
alert('Hello world');
}
function addExtraFields() {
// insert some checkboxes...
// register the events handler again for the newly added fields
registerCustomEventHandlers();
}
So basically inside the function that adds the check-boxes I register again all the events handlers. I was looking at something like this $(document).change(function() {}); but apperently it is not supported by all browsers...
Any suggestions?
You can delegate the event to the document instead so that they will be applied to all future inputs as well. You don’t even need to put it in a domReady event, since the document is always available:
$(document).on("change", "input.class_name", sayHelloWorld);
function sayHelloWorld() {
alert('Hello world');
}
function addExtraFields() {
// insert some checkboxes, they will have the same handler attached when inserted
}
Demo: http://jsfiddle.net/gdX3R/1/
I would recommend against using live selectors because of these reasons
Shortly summed up, it's a performance issue because it messes with every click event.
Instead, just use delegate like described in the post on lowest common parent element of the inputs (most likely a form):
$('#yourFormId').delegate('.class_name', 'click', function() {
// what you want to do
});
You can find a jsfiddle here
And don't use selectors like input.class_name (unless there elements other than input with that class name). They're slower than .class_name because they're looping through all the inputs in the form searching for elements with that class rather than just selecting by class.
As of jquery 1.4 you can always use live() too http://api.jquery.com/live/ which allows you to attach handlers now and in the future to any matching elements.
the way this is done in Jquery is such that you don't need the object to be present when creating the handler.
You could use:
$(document.body).on("click", "input[type='checkbox']",
function(){alert($(this))}
);
This will be applied to any new check-box added to the page, no matter timing.
I have a fairly standard star voting functionality in my app that uses jQuery's hover event. The partial that the star voting logic is in used to be rendered with the rest of the page once the DOM was initially loaded (HTML request). However, I would like to move the partial so that it's not loaded with the page but can be loaded when the user wants. I made a typical AJAX request to load the partial but when it gets rendered the stars don't react properly to events like a mouseover. Is this issue being brought on because I'm rendering the forms via AJAX or is there just a bug in my code? Thanks for the help
Update: Got it working using the on handler, thanks for the help all!
You are likely trying to bind events to nodes that don't exist in the DOM yet. The best way to solve this is to bind to a listener that exists prior to the Ajax request, that is an ancestor (sometimes incorrectly called "parent", which is only one level of ancestor) of the content being fetched. For example, given this markup in the page itself:
<div id="ajaxContainer">
<!-- content will be periodically replaced with Ajax -->
</div>
"ajaxContainer" is an ancestor of whatever you're going to fetch. Then you need to bind a listener using an appropriate method. In the old days you could use live() but it's deprecated and was not so efficient anyhow. Then the recommendation was for delegate(), which solved efficiency problems. Now it's for a delegated listener syntax of on(), which is roughly the same performance as delegate() but with different syntax.
All that to say, use .on() if you are using jQuery 1.7+.
Imagine your Ajax function retrieves a portion of a page containing your star system mouseover, which is inside a series of divs classed as "stars". The syntax might look like:
$(document).ready(function() {
$('#ajaxContainer').on('mouseenter', '.stars', function() {
$this = $(this); // cache this specific instance of a stars div as a jQuery object
// do stuff with $this
});
});
This says "Start listening inside ajaxContainer for events that match 'mouse enters stars divs' and when that happens, do stuff."
The elements that are created with Ajax will not respond to your event handlers, as the event handlers only work on elements that are present in the DOM at the time of initializiation.
You need to delegate, and listen for events on elements that are present in the DOM, and catch the bubbling of the dynamic elements.
You should use on() for this:
$('#nonDynamicElement').on('mouseenter', '#dynamicElement', function() {
//do stuff
});
As from jQuery 1.7+ you should use on()
for older versions of jquery you can use live()
jQuery has a function called live which lets you apply event handlers to not yet created objects.
As said in the comment, use on() instead.
If your using jQuery within an AJAX script, be sure to use jQuery instead of $.
jQuery( selector [, context] )
Instead of
$( selector [, context] )
With JQuery, is it possible to add an event listener to any element that currently, or will in the future, have a particular class?
I'm working on a project that makes heavy use of contentEditable, so the DOM is changing, and elements can have classes added and removed as a result of user input.
I would like to be able to say "elements of class X should do Y when clicked", but if I understand correctly, $(".X").click(Y) will only add the event listener to elements that currently have class X.
Furthermore, if an element is no-longer part of class X, then it will still have the click event listener.
How can I do this?
Yep. What you're talking about is called event delegation. Here's an example:
$('#container').on('click', '.innerElement', function(){
/// Do stuff
});
In your case, #container would be an element that is known to exist on page load which will contain the child elements you care about (either now or in the future). This approach takes advantage of event bubbling in the DOM.
As another poster mentioned, the live method will also work -- but it has been deprecated in jQuery 1.7, and is generally not as performant as using more selective delegation (such as the example above).
you'll want to use event delegation. jquery 1.7 has made this more abstract than previous versions, but it looks something like this:
$("#myWrappingElement").on("click", ".myclass", function(event){
alert($(this).text());
});
this basically adds a click event listener to the #myWrappingElement element, and jquery will automagically look to see what the original event target was and fire the proper function. this means you can add or remove .myclass elements and still have events fire on them.
the jQuery live() method swill allow to have a "live" action listener - so if new DOM elements match the selector, they will be attached to the action listener. For example:
$(".X").live("click", function(){
alert('some action');
});
See the documentation here for more info: http://api.jquery.com/live/
I'm not sure that the second part of your question about keeping the action listener attached after removing the class os possible - someone else might have a solution though.