You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

动态创建Bootstrap 4多级下拉菜单时$(this)点击事件失效

动态生成Bootstrap 4多级下拉菜单的点击事件失效问题

尝试实现Bootstrap 4多级下拉菜单,硬编码版本可正常工作,但动态生成菜单后,子菜单的点击事件无法为其添加show类,导致子菜单无法展开。

CSS 代码

.dropdown-submenu-new {
  position: relative;
}

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

.dropdown-submenu-new .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;
}

问题分析

  1. 事件绑定方式错误:动态生成的元素无法通过直接绑定事件触发,因为绑定事件时元素还未存在于DOM中。
  2. 类名不匹配:CSS中定义的类是.dropdown-submenu,但JS生成的子菜单项类是dropdown-submenu-new,导致样式失效;同时子菜单的dropdown-menu-new未包含Bootstrap原生的dropdown-menu类,缺失基础下拉样式。
  3. 变量未重置:循环生成顶级菜单时,li_class、a_class等变量在第一次添加dropdown类后未重置,导致后续所有菜单项都被错误添加下拉类。
  4. $(this)指向异常:事件绑定无效导致this无法正确指向当前点击元素,无法输出相关信息。

修复方案

1. 改为事件委托绑定事件

将事件绑定到静态父元素#navbarNavDropdown上,确保动态生成的元素能触发事件:

// 替换原事件绑定代码
$('#navbarNavDropdown').on('click', '.dropdown-menu-new a.dropdown-toggle', function(e) {
    e.stopPropagation();
    console.log($(this));
    if (!$(this).next().hasClass('show')) {
        $(this).parents('.dropdown-menu').first().find('.show').removeClass('show');
    }
    var $subMenu = $(this).next('.dropdown-menu');
    $subMenu.toggleClass('show');

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

    return false;
});

2. 统一类名并补充Bootstrap原生类

  • 保持CSS选择器与JS生成的类名一致(已在上方CSS代码中修正为dropdown-submenu-new)
  • 生成子菜单时,将dropdown-menu-new改为dropdown-menu dropdown-menu-new,继承Bootstrap的下拉菜单基础样式:
// 修改CreateDynamicList函数中的菜单类定义
var list_html = "<ul class='dropdown-menu dropdown-menu-new'" + idText + ">";

3. 重置循环变量避免类名污染

在每次循环顶级菜单项时,重置li_class、a_class、a_props,并给ID添加索引后缀避免重复:

// 替换原循环代码
for (var i = 0; i < data.length; i++) {
    // 每次循环重置变量
    let li_class = "nav-item";
    let a_class = "nav-link";
    let a_props = "";
    
    //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-" + i + "'";
    }

    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>";
}

4. 修正子菜单调用逻辑

移除冗余的判断条件,直接调用子菜单生成函数:

// 修改CreateDynamicList中的else分支
else
    list_html += "<li class='dropdown-submenu-new'><a class='dropdown-item dropdown-toggle'>" + data.child[i].title + "</a>" + CreateDynamicList(data.child[i], false) + "</li>";

最终效果

修复后,动态生成的多级下拉菜单可以正常展开/收起,点击事件能正确为子菜单添加show类,样式也能正常渲染。

内容的提问来源于stack exchange,提问作者Samra

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.22 11:38:10