2

I am trying to dynamically create this bootstrap 4 dropdown (see answer by @Gerhard Gotz).

The hard coded list works for me, however, I am trying to dynamically create this list and then populate it.

CSS:

.dropdown-submenu {
 position: relative;
}

.dropdown-submenu a::after {
transform: rotate(-90deg);
position: absolute;
right: 6px;
top: .8em;
}

.dropdown-submenu .dropdown-menu {
 top: 0;
left: 100%;
margin-left: .1rem;
margin-right: .1rem;
}

HTML:

<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-beta/css/bootstrap.min.css" integrity="sha384-/Y6pD6FV/Vv2HJnA6t+vslU6fwYXjCFtcEpHbNJ0lyAFsXTsjBbfaDjzALeQsN6M" crossorigin="anonymous">
<div class="collapse navbar-collapse" id="navbarNavDropdown">
</div>

Javascript:

$(document).ready(function () {
var data = [
    {
        "title": "Home",
        "child": null
    },
    {
        "title": "Dropdown link",
        "child": [
            {
            "title": "Action",
            "child": null
            },
            {
            "title": "Another Action",
            "child": null
            },
            {
                "title": "Submenu",
                "child": [{
                    "title": "Submenu action",
                    "child": null
                },
                    {
                        "title": "Another submenu action",
                        "child": null
                    },
                    {
                        "title": "Subsubmenu",
                        "child": [{
                            "title": "Subsubmenu action",
                            "child": null
                        },
                            {
                                "title": "Another subsubmenu action",
                                "child": null
                            }
                        ]
                    },
                    {
                        "title": "Second subsubmenu",
                        "child": [{
                            "title": "Subsubmenu action",
                            "child": null
                        },
                            {
                                "title": "Another subsubmenu action",
                                "child": null
                            }]
                    }
                ]
            }
        ]
    }
];

var list_html = "<ul class='navbar-nav'>";
var li_class = "nav-item";
var a_class = "nav-link";
var a_props = "";

for (var i = 0; i < data.length; i++) {
    //if (i == 0)
    //    li_class += " active";
    
    if (data[i].child != null) {
        li_class += " dropdown";
        a_class += " dropdown-toggle";
        a_props = " data-toggle='dropdown' id='navbarDropdownMenuLink'";
    }

    list_html += "<li class='" + li_class + "'><a class='" + a_class + "'" + a_props + ">" + data[i].title + "</a>"; 

    if (data[i].child != null) {
        list_html += CreateDynamicList(data[i], true);

    }

    list_html += "</li>";
}
list_html += "</ul>";
console.log(list_html);

$("#navbarNavDropdown").html(list_html);

$('.dropdown-menu-new a.dropdown-toggle').on('click', function (e) {
    console.log("here");
    console.log($(this));
    if (!$(this).next().hasClass('show')) {
        $(this).parents('.dropdown-menu-new').first().find('.show').removeClass('show');
    }
    var $subMenu = $(this).next('.dropdown-menu-new');
    $subMenu.toggleClass('show');


    $(this).parents('li.nav-item.dropdown.show').on('hidden.bs.dropdown', function (e) {
        $('.dropdown-submenu-new .show').removeClass('show');
    });


    return false;
});
}); // end document ready

function CreateDynamicList(data, isMainFunction) {
var idText = "";

if (isMainFunction)
    idText += " aria-labelledby='navbarDropdownMenuLink'";

var list_html = "<ul class='dropdown-menu-new'" + idText + ">";

//console.log(data);
//console.log(data.child);
if (data.child != null)
    for (var i = 0; i < data.child.length; i++) {
        //if child has no child then else
        if (data.child[i].child == null)
            list_html += "<li><a class='dropdown-item'>" + data.child[i].title + "</a></li>";
        else
            list_html += "<li class='dropdown-submenu-new'><a class='dropdown-item dropdown-toggle'>" + data.child[i].title + "</a>" + (data.child != null ? CreateDynamicList(data.child[i], false) : "") + "</li>";

        console.log("loop: " + data.child[i].title);
    }
else
    list_html += "<li><a class='dropdown-item'>" + data.title + "</a></li>";

list_html += "</ul>";
//console.log(list_html);
return list_html;
}

The list is correctly recreated, however, the show class is not being attached on click event. So, particularly the following code is not working:

$('.dropdown-menu-new a.dropdown-toggle').on('click', function (e) {
    console.log("here");
    console.log($(this));
    if (!$(this).next().hasClass('show')) {
        $(this).parents('.dropdown-menu-new').first().find('.show').removeClass('show');
    }
    var $subMenu = $(this).next('.dropdown-menu-new');
    $subMenu.toggleClass('show');


    $(this).parents('li.nav-item.dropdown.show').on('hidden.bs.dropdown', function (e) {
        $('.dropdown-submenu-new .show').removeClass('show');
    });


    return false;
});

Also note, "here" is always console logged on click, but $(this) is not!

5
  • TL;DR use $('#navbarNavDropdown').on('click', '.dropdown-menu-new a.dropdown-toggle', function(e) { ... })
    – Phil
    Commented Jun 12 at 4:29
  • Let me know if that doesn't answer your question and I'll re-open it
    – Phil
    Commented Jun 12 at 4:31
  • hi not exactly, the inside code is still not working as it all depends on $(this)..guess I need the work around for all of that
    – Samra
    Commented Jun 12 at 4:42
  • I have copied it from the shared link but and trying to debug or work around this one..trying to achieve: adding and removing show class each time
    – Samra
    Commented Jun 12 at 5:56
  • if only i can grab the current li element that has been clicked rest will be easy for me
    – Samra
    Commented Jun 12 at 5:57

2 Answers 2

1

You need to use event delegation concept to make your code work.

I have added additional CSS to show you that the code worked.

Working snippet:

$(document).ready(function() {
  var data = [{
      "title": "Home",
      "child": null
    },
    {
      "title": "Dropdown link",
      "child": [{
          "title": "Action",
          "child": null
        },
        {
          "title": "Another Action",
          "child": null
        },
        {
          "title": "Submenu",
          "child": [{
              "title": "Submenu action",
              "child": null
            },
            {
              "title": "Another submenu action",
              "child": null
            },
            {
              "title": "Subsubmenu",
              "child": [{
                  "title": "Subsubmenu action",
                  "child": null
                },
                {
                  "title": "Another subsubmenu action",
                  "child": null
                }
              ]
            },
            {
              "title": "Second subsubmenu",
              "child": [{
                  "title": "Subsubmenu action",
                  "child": null
                },
                {
                  "title": "Another subsubmenu action",
                  "child": null
                }
              ]
            }
          ]
        }
      ]
    }
  ];

  var list_html = "<ul class='navbar-nav'>";
  var li_class = "nav-item";
  var a_class = "nav-link";
  var a_props = "";

  for (var i = 0; i < data.length; i++) {
    if (data[i].child != null) {
      li_class += " dropdown";
      a_class += " dropdown-toggle";
      a_props = " data-toggle='dropdown' id='navbarDropdownMenuLink'";
    }

    list_html += "<li class='" + li_class + "'><a class='" + a_class + "'" + a_props + ">" + data[i].title + "</a>";

    if (data[i].child != null) {
      list_html += CreateDynamicList(data[i], true);

    }

    list_html += "</li>";
  }
  list_html += "</ul>";

  $("#navbarNavDropdown").html(list_html);

  function CreateDynamicList(data, isMainFunction) {
    var idText = "";

    if (isMainFunction)
      idText += " aria-labelledby='navbarDropdownMenuLink'";

    var list_html = "<ul class='dropdown-menu-new'" + idText + ">";


    if (data.child != null)
      for (var i = 0; i < data.child.length; i++) {
        //if child has no child then else
        if (data.child[i].child == null)
          list_html += "<li><a class='dropdown-item'>" + data.child[i].title + "</a></li>";
        else
          list_html += "<li class='dropdown-submenu-new'><a class='dropdown-item dropdown-toggle'>" + data.child[i].title + "</a>" + (data.child != null ? CreateDynamicList(data.child[i], false) : "") + "</li>";
      }
    else
      list_html += "<li><a class='dropdown-item'>" + data.title + "</a></li>";

    list_html += "</ul>";
    return list_html;
  }
});

$("#navbarNavDropdown").on('click', '.dropdown-menu-new a.dropdown-toggle', function(e) {
  if (!$(this).next().hasClass('show')) {
    $(this).parents('.dropdown-menu-new').first().find('.show').removeClass('show');
  }
  var $subMenu = $(this).next('.dropdown-menu-new');
  $subMenu.toggleClass('show');

  return false;
});

$('li.dropdown a').on('hidden.bs.dropdown', function(e) {
  alert('here');
  $('.dropdown-submenu-new').toggle();
});
.dropdown-submenu {
  position: relative;
}

.dropdown-submenu a::after {
  transform: rotate(-90deg);
  position: absolute;
  right: 6px;
  top: .8em;
}

.dropdown-submenu .dropdown-menu {
  top: 0;
  left: 100%;
  margin-left: .1rem;
  margin-right: .1rem;
}

.show {
  color: red;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.7.1/jquery.min.js"></script>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-beta/css/bootstrap.min.css" integrity="sha384-/Y6pD6FV/Vv2HJnA6t+vslU6fwYXjCFtcEpHbNJ0lyAFsXTsjBbfaDjzALeQsN6M" crossorigin="anonymous">
<div class="navbar-collapse" id="navbarNavDropdown">
</div>

Note: instead of .toggleClass('show') use .toggleClass('collapse') to make drop-downs open/close perfectly.

1
  • This one looks good to me and am marking it as answer, my code works standalone in jsfiddler but not in the application. So something to do with other code around!!.. I will also update in comments once I find it
    – Samra
    Commented Jun 12 at 6:49
0

Ok so I found the issue with my code was that the first tier was built differently and had different classes. So, while the rest of the code was working as expected but the first one was not (which felt like nothing was working).

Also, my .min file was not being updated for a while as I was updating and checking my code. For this issue, I cleaned and rebuilt my code.

The answers given by both @Phil and @Death-is-the-real-truth are still correct. I have added the following pieces of code to make it work in document.ready function.

$('#navbarNavDropdown').on('click', '.nav-item a.nav-link', function (e) {
    console.log($(this).next('.dropdown-menu-new').hasClass('show'));

    if (!$(this).next().hasClass('show')) {
        $(this).parents('.dropdown-menu-new').first().find('.show').removeClass('show');
    }
    var $subMenu = $(this).next('.dropdown-menu-new');
    $subMenu.toggleClass('show');



    $(this).parents('li.nav-item.dropdown.show').on('hidden.bs.dropdown', function (e) {
        $('.dropdown-submenu-new .show').removeClass('show');
    });


    return false;
});
$('.dropdown-menu-new a.dropdown-toggle').on('click', function (e) {
    console.log($(this).next('.dropdown-menu-new').hasClass('show'));

    if (!$(this).next().hasClass('show')) {
        $(this).parents('.dropdown-menu-new').first().find('.show').removeClass('show');
    }
    var $subMenu = $(this).next('.dropdown-menu-new');
    $subMenu.toggleClass('show');

    

    $(this).parents('li.nav-item.dropdown.show').on('hidden.bs.dropdown', function (e) {
        $('.dropdown-submenu-new .show').removeClass('show');
    });


    return false;
});

Not the answer you're looking for? Browse other questions tagged or ask your own question.