1

In my web application I use .on() from jQuery to bind click event to a specific li elements in a dynamic multilevel menu created by an AJAX call and passed to the DOM.

$(".dl-menu li:not(:has(li)):not(.dl-back)").on("click", function(){
  // My Code...
});

Obviously I put this code behind all the dependencies (jQuery in this case), but I does not work, the code is not being executed.

Only works if I open Firebug and paste the code in it.

I also have tried this:

$(document).ready(function() {
  $(".dl-menu li:not(:has(li)):not(.dl-back)").on("click", function(){
    // My Code...
  });
});

But it does not work too.

What I'm doing wrong?

EDIT: To add more details:

HTML structure:

  <div id='tab2' class='col s12'>
    <div class='section no-pad-bot' id='index-banner'>
      <br><br>
      <h2 class='header center green-text'>MENU</h2>
      <div id='prodcontainer'>
        <div id='dl-menu' class='dl-menuwrapper'></div>
      </div>
    </div>
  </div>

AJAX: (the important parte - the code inside success

$(".dl-menuwrapper").html("<button class='dl-trigger' style='visibility:hidden;'></button>"+json.html.replace('dl-submenu', 'dl-menu dl-menuopen'));
$('#dl-menu').dlmenu();

The json.html contains the string with the HTML to being added to the DOM. Like this:

json.html='<ul class="dl-submenu"><li data-id="17"><a href="#" class="catlink">A</a><ul class="dl-submenu"><li data-id="18"><a href="#" class="catlink">B</a><ul class="dl-submenu"><li data-id="20"><a href="#" class="catlink">C</a></li><li data-id="21"><a href="#" class="catlink">D</a></li></ul></li><li data-id="19"><a href="#" class="catlink">E</a></li></ul></li></ul>'
candlejack
  • 1,189
  • 2
  • 22
  • 51

2 Answers2

3

Using .on() this way only binds elements that are currently in the DOM. Because you load the menu through AJAX, it is not available when this code is executed.

The best workaround is to select a wrapper and specify a selector:

$(".dl-menu").on("click", "li:not(:has(li)):not(.dl-back)", function(){

});

Providing .dl-menu exists in the HTML that you do not create once the document is loaded.

EDIT - Alternative

Place the code setting up the event listener after the menu is loaded.

nicovank
  • 3,157
  • 1
  • 21
  • 42
  • I also have tried that and don't work... The `.dl-menu` are also created via AJAX. Note that if I use that wat to `select` the `li`s does not even work in Firebug... – candlejack Jan 20 '17 at 01:51
  • In that case, replace the first selector by the one you're using to load the menu, and add `.dl-menu` in the second one. – nicovank Jan 20 '17 at 01:54
  • I also tried that! argh! Please, see my updated question, it has more details. – candlejack Jan 20 '17 at 01:59
  • Your final suggestion works, I put my code after this line `$('#dl-menu').dlmenu();` in the AJAX's `success` function, but it look ugly, do you know how to prevent this o handle this issue? – candlejack Jan 20 '17 at 02:37
  • Maybe the problem comes from the CSS `:has`, which has poor support in browsers. Could you try using a selector like this: `$(".dl-menuwrapper").on("click", ".dl-menu li > li:not(.dl-back)", ...` which selects only the direct children? – nicovank Jan 20 '17 at 02:38
  • `CSS`? What does CSS have to do with jQuery? I don't understand, also, `"dl-menu li > li:not(.dl-back)"` is not equivalent to `".dl-menu li:not(:has(li)):not(.dl-back)"`. – candlejack Jan 20 '17 at 02:40
  • You are using CSS selectors when you use jQuery. Now according to the [`:has()` documentation](https://developer.mozilla.org/en-US/docs/Web/CSS/:has), it has no support. So your code might not be working because of the way `:has()` is implemented in your browser. -- Yes these two selectors are different, but in that case they should both be able to make it work. And `>` is widely supported by browsers. – nicovank Jan 20 '17 at 02:42
  • They are `jQuery` selectors, according to: https://api.jquery.com/has-selector/ Because `:has()` is a jQuery extension ***and not part of the CSS specification***, queries using :has() cannot take advantage of the performance boost provided by the native DOM `querySelectorAll()` method. – candlejack Jan 20 '17 at 02:48
  • Didn't know jQuery had a built-in `:has()`. Sorry! If you look at [this pen](http://codepen.io/nicovank/pen/GrWrGx), it works perfectly. – nicovank Jan 20 '17 at 02:59
  • Well, this really works, I will mark as solved, but would be awesome if I must not put my code into the AJAX, I have the async process. Argh! – candlejack Jan 20 '17 at 04:00
-1

You should use .on('click'....) on an element that's already present. So you will need to use the code like:

$(document).ready(function() {
  $(".dl-menuwrapper").on("click", ".dl-menu li:not(:has(li)):not(.dl-back)", function(){
    // My Code...
  });
});

Or alternatively,

$(document).on("click", ".dl-menu li:not(:has(li)):not(.dl-back)", function(){
    // My Code...
});
Vivek Athalye
  • 2,974
  • 2
  • 23
  • 32
  • I have tried that and don't work. I use the `".dl-menu li:not(:has(li)):not(.dl-back)"` properly because ***it works if I run the code in the JavaScript console developer... – candlejack Jan 20 '17 at 02:24
  • Yes, it does not even work on firebug, but this works in firebug`$(".dl-menu li:not(:has(li)):not(.dl-back)").on("click", function(){ // My Code... });` – candlejack Jan 20 '17 at 02:29
  • I'm going crazy with this issue – candlejack Jan 20 '17 at 02:31
  • If I run `$(document).on(...)` I got: `Object[Document #]`. But if I run `$(".dl-menu li:not(:has(li)):not(.dl-back)").on("click", function(){ ... });` I got this: `Object[li, li, li]` and it work in that way... – candlejack Jan 20 '17 at 02:33