Issue with .on in dealing with appended nodes - javascript

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/

Related

Passing an jQuery element cache to a method for modification

Problem Fiddle:
Click To View (Attempt #1, using Delegated Events)
Click To View (Attempt #2, brute force approach as below)
Click To View (Attempt #3, refactored, has problem I am trying to solve)
On a project I'm working with, I'm exploring a rather dynamic form. In addition to some static elements, there are various interactive elements, which can be cloned from a hidden 'template' markup set and added at various points in the business process.
Because of the dynamic nature, my tried-and-true method of setting up a jQuery element cache and event handlers on-load, then letting the user do whatever isn't working out, because of this dynamic nature; I was finding that my dynamically-added elements had no click events.
To solve this problem, I manually set up a rebind method for each scripted element in question. The rebind process involves A) re-acquiring the set of elements for a given descriptive selector, B) dropping any existing events on that cache, as those events apply to an incomplete element set, and C) calling a bind method to apply the new events to the entire set.
The brute-force, working way that I got, had this going on:
var $elementCache = $('.some-class');
function rebindSomeLink() {
// Re-acquire the element cache...
$elementCache = $('.some-class');
// Drop all existing events on the cache...
$elementCache.unbind();
// Call a bind function to establish new events.
bindSomeLink();
}
function bindSomeLink() {
$elementCache.click(function (e) {
// ...Behavior...
});
}
// There are four other links with a similar rebind/bind function relationship set up.
Naturally, I seized on the rebind being repeated so often with nearly the exact same code - ripe for a refactor. We have a common library namespace, where I added a rebindEvents function...
var MyCommon = function () {
var pub = {};
pub.rebindEvents = function($elementCache, selector, bindFunction) {
$elementCache = $(selector);
$elementCache.unbind();
bindFunction();
};
return pub;
}();
Upon trying to call that, and run the site, I immediately stubbed my toe on an UncaughtTypeError: method click cannot be called on object undefined.
As it turns out, it seems when I call the following:
MyCommon.rebindEvents($elementCache, '.some-class', bindSomeLink);
The $elementCache is not being passed to the rebindEvents method; when I step to it in my debugger, $elementCache inside of rebindEvents is undefined.
Some handy StackOverflow research revealed to me that JavaScript does not have referential-passing, at least in the C/C++/C# sense that I am familiar with, which leads me to my two Questions:
A) Is it even possible for me to refactor this rebind functionality with a cache reference pass of some sort?
B) If it's possible for me to refactor my rebind function to my common namespace, how would I go about doing it?
Use Jquery On to bind events at an element high-up in the DOM that is always present.
http://api.jquery.com/on/
This is the simplest way to handle event binding to dynamically created elements.
here is a jsfiddle that shows an example.
$('#temp').on('click', 'button', function(){
alert('clicked');
});
$('#temp').append('<button>OK</button>');
The event is bound to a div, which later has a button dynamically added. Because the button has no click event, the event "bubbles" up the DOM tree to its parent element which does have a click event for a button, so it handles it and the event "bubbles" no further.

jQuery event delegation

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.

Using jQuery delegate (on) with a check box causes huge delay in response - why?

Hi I have a dynamically create table which acts as a pick list using check boxes. I Want these check boxes to be mutually exclusive. So upon checking a box I need to clear any other checked boxes.
$(document).on("keydown", "#list_Instructors input.allocate",function(event){
alert("hit");
$("#list_Instructors input.allocate").removeAttr('checked');
$(event.target).attr('checked', 'checked');
});
This sort of works but there is a huge delay between clicking and anything happening which is no good. I have tried all sorts of combinations with no success.
Is there is simple explanation as to why this is creating a delay.
Your problem is you bind on method for whole DOM which is really BAD.
So always try to bind that to the closest div (closest parent element) which your controls are exist.
And second thing is always cache your selectors for better performance.Like below
var dataTable=$('#dataTable');
dataTable.on("click", function(event){
alert($(this).text());
});
About Event performance from Jquery API says like below.
Attaching many delegated event handlers near the top of the document
tree can degrade performance. Each time the event occurs, jQuery must
compare all selectors of all attached events of that type to every
element in the path from the event target up to the top of the
document. For best performance, attach delegated events at a document
location as close as possible to the target elements. Avoid excessive
use of document or document.body for delegated events on large
documents.
What you might be seeing is that until the alert box is dismissed, the code afterwards is not executed. The alert command is a blocking one.
Perhaps you can use console.log() for debugging purposes of this feature. This will not block your code and it will be executed on the keydown event.
You need to use $(this) instead of going through another lookup. Also as stated above try to bind to the closest parent element if possible, for example a container div. With that said this should speed you up a bit:
$(document).on('keydown', '#list_Instructors input.allocate', function (event) {
//alert("hit");
console.log('hit');
$(this).removeAttr('checked');
$(event.target).attr('checked', 'checked');
});
But you should try to replace document with a container div or another parent element

How to register events handler after the page load?

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.

How to get dynamically loaded HTML to be parsed using Javascript Jquery?

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.

Categories

Resources