user2305672 user2305672 - 1 month ago 5
Javascript Question

javaScript doesn't run when loaded by ajax

I have a javaScript function in

view/loadMoreDataWater.php


<td ondblclick="updateCell()">
</td>


Now in
table.php
in
public_html
folder I have a table when onscroll is supposed to load more data:

qlimit =0;
$("#waterDBTable tbody").scroll(function() {

if ($("#waterDBTable").scrollTop() > $("#waterDBTable").height() - 300) {
qlimit = qlimit + 5;
$("#loadingDiv").css('display','block');
$.ajax({
url: "controllers/loadMoreDataWater.php?qlimit=" + qlimit + "&table=water",
success: function(result) {
$("#waterDBTable").append(result);
$("#loadingDiv").css('display','none');
}
});
}

});


Now when the content is loaded by ajax. ondblclick function is not working.
I found something about using

$(body).on('click', '#something', function() {


but I got confused and it didn't work for me, I didn't know in which file I must place such code.

Thank you

Answer

You were reading about event delegation.

$('body').on('click', '#something', function() { 
  // do stuff
});

This code would add an event listener to the body element, listening for a click event and attaching a handler function to any click event which bubbled up from an element with an id of 'something'.

The idea is instead of having an ondblclick= attribute, you attach one event listener like this:

$('body').on('dblclick', 'td', function() {
  // do whatever updateCell() does, using `$(this)` for the td that was clicked
});

So in effect you're delegating the event handler to one element. This is useful for several reasons, chief among them is that the body element doesn't go anywhere when you remove content and neither does the event handler need to be attached to new elements when being added to the DOM.

To understand how this works, reverse engineer it. Imagine what this is actually doing: all events bubble up from their source to the top level node. This means that every click event registers as a click on the event target (the element you're adding an event handler to) and on the body element, because you clicked the body too in clicking the element.

"So", one might wonder, "why add many event listeners to many individual elements when you can just add one giant event listener to the body and implement some switchboard logic in the event handler function?"

Without jQuery, you can do a basic event dispatcher like so:

document.body.addEventListener('dblclick', function(){
  var el = this;
  if ( el.classList.contains('dblclick-to-update') ){
    updateCell(el);
  }

  // other if statements could be below, handling other dblclick events, on elements other than td cells, if necessary
});

So, instead of saying:

  • If this cell is clicked
    • Update it.

potentially dozens or even hundreds of times, we say:

  • If there is a click anywhere in the page,
    • check what was clicked,
      • if it's something we want to update,
        • fire the updateCell function with the cell to update.

Ideally, each of your td elements would be given a CSS class like class="dblclick-update" or a data- attribute like data-dblclickupdate="true", either of which you could then use as a selector for your delegated event handler. This would be instead of using 'td' as a selector, which would screw up if you have other td elements which do not need to have the updateCell() behaviour attached to them.

So your code might look like this:

$('body').on('dblclick', '.dblclick-update', function(){
  updateCell($(this));
});

Using updateCell($(this)) will give you the td which was clicked, but you'll need to change the updateCell() function to work with the element being passed in as a parameter. In your function definition, you'll need firstly to state that the function expects a parameter, so where you probably have this:

function updateCell() {
 // do stuff to 'this'
}

you need to define the function like this instead:

function updateCell(cell) {
  // do stuff to 'cell', which is passed in as a parameter now
}

Obviously, if you choose to use event delegation, you won't need those ondblclick= attributes anymore, so remove those and just have <td class="dblclick-update"></td> or something like that.

As for why the ondblclick="updateCell()" wasn't working for newly added td elements, my guess would be that you defined your updateCell() function within a $(document).ready block, which means that the function is not available to newly added elements because it's not in the global scope.

Try putting the function in the global scope instead, either by defining updateCell() outside of $(document).ready (and outside of any other functions for that matter) or by referencing it directly in the global window object like this:

window.updateCell = function(){ 
  // do stuff to cell
};

Or, if you use event delegation,

window.updateCell = function(cell){ 
  // do stuff to cell
}; 
Comments