子元素的 click 事件被父元素的 event.stopPropagation 阻止

Child element's click event prevented by parent's event.stopPropagation

提问人:Rocky Barua 提问时间:7/20/2023 更新时间:7/30/2023 访问量:115

问:

在我的(实时站点)上,我使用“nice-select”库创建了一个选择元素。此 select 元素嵌套在下拉菜单中,该下拉菜单具有自己的 JavaScript 文件

    function singleMenu(targetId, menuId, show = false) {
    const targetElement = document.getElementById(targetId);
    const menuElement = document.getElementById(menuId);

    // Initial state
    if (show) {
        // show dropdown
        menuElement.style.display = "block";
        targetElement.classList.add("active");
    } else {
        // hide dropdown
        menuElement.style.display = "none";
        targetElement.classList.remove("active");
    }

    // Toggle menu visibility when target element is clicked
    targetElement.addEventListener("click", () => {
        show = !show;

        if (show) {
            // show dropdown
            menuElement.style.display = "block";
            targetElement.classList.add("active");
        } else {
            // hide dropdown
            menuElement.style.display = "none";
            targetElement.classList.remove("active");
        }
    });

    // Close menu if clicked outside of container
    document.addEventListener("click", (event) => {
        if (!targetElement.contains(event.target)) {
            show = false;
            menuElement.style.display = "none";
            targetElement.classList.remove("active");
        }
    });

    // Prevent menu from closing when clicked inside the menu element
    menuElement.addEventListener("click", function (event) {
        event.stopPropagation();
    });

    // Calculate half of the targetElement width
    const targetHalfWidth = targetElement.offsetWidth / 2;

    // Set a CSS variable with the half width value
    targetElement.style.setProperty(
        "--target-half-width",
        targetHalfWidth + "px"
    );
}

目前,我面临的问题是,单击时选择元素无法打开,但在使用回车键和向上/向下按钮时,它确实可以正常工作。

我怀疑问题可能与 event.stopPropagation() 函数有关,因为当我删除该行时,会添加 select 元素的“open”类(如检查工具所示)。但是,删除 event.stopPropagation() 行也会导致下拉菜单立即关闭。

我的目标是找到一种解决方案,允许我在不关闭下拉菜单的情况下打开选择元素。我将不胜感激关于如何实现这一目标的任何见解或建议。

以下是相关的HTML代码(html文件代码)供参考。感谢您的帮助!

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="UTF-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1.0" />
        <link
            rel="stylesheet"
            id="hello-elementor-child-style-css"
            href="https://fs.codelinden.com/wp-content/themes/hello-child/style.css?ver=3"
            media="all"
        />
        <link rel="stylesheet" href="header.css" />
        <script src="header.js" defer></script>

        <!-- single-menu dropdown style -->
        <link rel="stylesheet" href="single-menu-dropdown.css" />

        <!-- custom form element style (nice-select) -->
        <link rel="stylesheet" href="form.css" />

        <!-- nice-select style-->
        <link
            rel="stylesheet"
            href="https://cdnjs.cloudflare.com/ajax/libs/jquery-nice-select/1.1.0/css/nice-select.min.css"
            integrity="sha512-CruCP+TD3yXzlvvijET8wV5WxxEh5H8P4cmz0RFbKK6FlZ2sYl3AEsKlLPHbniXKSrDdFewhbmBK5skbdsASbQ=="
            crossorigin="anonymous"
            referrerpolicy="no-referrer"
        />

        <title>Header</title>
    </head>
    <body>
        <header class="header HruDj">
            <div class="nav-dropdown target-id DeYlt" id="target_id1">
                <p>toggle dropdown</p>

                <!-- single-menu dropdown container -->
                <div id="menu_id1" class="menu-id">
                    <div class="form-field full-width">
                        <input
                            type="hidden"
                            name="form_my_contact_form"
                            value="1"
                        />
                        <label for="product_type">Product Type</label>
                        <select
                            name="product_type"
                            id="product_type"
                            required=""
                        >
                            <option value="book">Book</option>
                            <option value="movie">Movie</option>
                            <option value="music">Music</option>
                            <option value="" disabled="">
                                Select a product type
                            </option>
                        </select>
                    </div>
                </div>
            </div>
            <div class="nav-dropdown target-id DeYlt" id="target_id2">
                <p>toggle dropdown</p>

                <!-- single-menu dropdown container -->
                <div id="menu_id2" class="menu-id">
                    <div class="form-field full-width">
                        <input
                            type="hidden"
                            name="form_my_contact_form"
                            value="1"
                        />
                        <label for="product_type">Product Type</label>
                        <select
                            name="product_type"
                            id="product_type"
                            required=""
                        >
                            <option value="book">Book</option>
                            <option value="movie">Movie</option>
                            <option value="music">Music</option>
                            <option value="" disabled="">
                                Select a product type
                            </option>
                        </select>
                    </div>
                </div>
            </div>
        </div>
        </header>

        <!-- jquery cdn -->
        <script src="popup/jquery/jquery.min.js"></script>

        <!-- single-menu dropdown script -->
        <script src="single-menu-dropdown.js"></script>

        <!-- nice-select script -->
        <script
            src="https://cdnjs.cloudflare.com/ajax/libs/jquery-nice-select/1.1.0/js/jquery.nice-select.min.js"
            integrity="sha512-NqYds8su6jivy1/WLoW8x1tZMRD7/1ZfhWG/jcRQLOzV1k1rIODCpMgoBnar5QXshKJGV7vi0LXLNXPoFsM5Zg=="
            crossorigin="anonymous"
            referrerpolicy="no-referrer"
        ></script>

        <!-- initialize dropdown and select -->
        <script>
            // Call singleMenu function for each menu
            singleMenu("target_id1", "menu_id1", false);
            singleMenu("target_id2", "menu_id2", false);

            $(document).ready(function () {
                // Apply the niceSelect plugin to all select elements
                $("select").niceSelect();
            });
        </script>
    </body>
</html>
JavaScript 事件冒泡 停止传播

评论


答:

1赞 PCDSandwichMan 7/23/2023 #1

我很确定这只是由于事件传播顺序而发生的。单击时,这就是我所看到的:select

  1. 上的 click 事件触发该函数。selectniceSelect
  2. 然后,click 事件会冒泡到父元素,最终到达 your 并在添加 .menuElementevent.stopPropagation()

我相信这是一个问题的原因是因为它不会在单击时立即解决,并且它会在执行其操作之前等待下一个事件循环。但是,由于事件发生之前,就已经发生了,因此请阻止这种情况发生。niceSelectstopPropagation()stopPropagation()

解决此问题的一种可能非正统的方法是像这样延迟您的停止:stopPropagation()

menuElement.addEventListener("click", function (event) {
  setTimeout(() => event.stopPropagation(), 0);
});

通俗地说,这告诉 javascript“在事件循环完成后立即运行此代码,但在下一个循环开始之前”。

0赞 Hao Wu 7/23/2023 #2

将事件侦听器添加到 时,尝试通过提供第三个属性 来指定 useCapture。所以它在 的 click 事件之前触发:targetElementtruetruemenuElement

// Toggle menu visibility when target element is clicked
targetElement.addEventListener('click', () => {
    show = !show;

    if (show) {
        // show dropdown
        menuElement.style.display = 'block';
        targetElement.classList.add('active');
    } else {
        // hide dropdown
        menuElement.style.display = 'none';
        targetElement.classList.remove('active');
    }
}, true);  // set useCapture to true
1赞 deepanshu223 7/30/2023 #3

因此,我能够达到预期的结果,但进行了一些小的更改。 似乎没有任何方法可以始终如一地工作。event.stopPropagation()

您可以做的是,与其将整个菜单父级设置为单击处理程序,不如将其设置为在更直接的目标上工作。

这意味着,而不是使用

 <div class="nav-dropdown target-id DeYlt" id="target_id1">
     <p>toggle dropdown</p>

您可能希望将目标 ID 添加到里面的 p 标签中。

 <div class="nav-dropdown target-id DeYlt">
     <p id="target_id1">toggle dropdown</p>

menu_id2和target_id2也是如此。

这将需要对 javascript 进行一些小的更改。 更改在单击外部处理时关闭菜单的单击处理程序,而不是考虑目标现在在菜单内。targetElement.parentElement

 // Close menu if clicked outside of container
    document.addEventListener("click", (event) => {
        if (!targetElement.parentElement.contains(event.target)) {
            show = false;
            menuElement.style.display = "none";
            targetElement.classList.remove("active");
        }
});

删除菜单停止传播项,因为它不再需要。

menuElement.addEventListener("click", function (event) {
    event.stopPropagation();

总而言之,这是您的问题中的更新代码的样子,它应该按预期工作。

function singleMenu(targetId, menuId, show = false) {
    const targetElement = document.getElementById(targetId);
    const menuElement = document.getElementById(menuId);

    // Initial state
    if (show) {
        // show dropdown
        menuElement.style.display = "block";
        targetElement.classList.add("active");
    } else {
        // hide dropdown
        menuElement.style.display = "none";
        targetElement.classList.remove("active");
    }

    // Toggle menu visibility when target element is clicked
    targetElement.addEventListener("click", () => {
        show = !show;

        if (show) {
            // show dropdown
            menuElement.style.display = "block";
            targetElement.classList.add("active");
        } else {
            // hide dropdown
            menuElement.style.display = "none";
            targetElement.classList.remove("active");
        }
    });

    // Close menu if clicked outside of container
    document.addEventListener("click", (event) => {
        if (!targetElement.parentElement.contains(event.target)) {
            show = false;
            menuElement.style.display = "none";
            targetElement.classList.remove("active");
        }
    });

    // Prevent menu from closing when clicked inside the menu element
   /* menuElement.addEventListener("click", function (event) {
        event.stopPropagation();
    });*/

    // Calculate half of the targetElement width
    const targetHalfWidth = targetElement.offsetWidth / 2;

    // Set a CSS variable with the half width value
    targetElement.style.setProperty(
        "--target-half-width",
        targetHalfWidth + "px"
    );
}


 singleMenu("target_id1", "menu_id1", false);
 singleMenu("target_id2", "menu_id2", false);
 $(document).ready(function () {
   // Apply the niceSelect plugin to all select elements
   $("select").niceSelect();
 });
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="UTF-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1.0" />
        <link
            rel="stylesheet"
            id="hello-elementor-child-style-css"
            href="https://fs.codelinden.com/wp-content/themes/hello-child/style.css?ver=3"
            media="all"
        />
        <link rel="stylesheet" href="header.css" />
        <script src="header.js" defer></script>

        <!-- single-menu dropdown style -->
        <link rel="stylesheet" href="single-menu-dropdown.css" />

        <!-- custom form element style (nice-select) -->
        <link rel="stylesheet" href="form.css" />

        <!-- nice-select style-->
        <link
            rel="stylesheet"
            href="https://cdnjs.cloudflare.com/ajax/libs/jquery-nice-select/1.1.0/css/nice-select.min.css"
            integrity="sha512-CruCP+TD3yXzlvvijET8wV5WxxEh5H8P4cmz0RFbKK6FlZ2sYl3AEsKlLPHbniXKSrDdFewhbmBK5skbdsASbQ=="
            crossorigin="anonymous"
            referrerpolicy="no-referrer"
        />

        <title>Header</title>
    </head>
    <body>
        <header class="header HruDj">
            <div class="nav-dropdown target-id DeYlt">
                <p id="target_id1">toggle dropdown</p>

                <!-- single-menu dropdown container -->
                <div id="menu_id1" class="menu-id">
                    <div class="form-field full-width">
                        <input
                            type="hidden"
                            name="form_my_contact_form"
                            value="1"
                        />
                        <label for="product_type">Product Type</label>
                        <select
                            name="product_type"
                            id="product_type"
                            required=""
                        >
                            <option value="book">Book</option>
                            <option value="movie">Movie</option>
                            <option value="music">Music</option>
                            <option value="" disabled="">
                                Select a product type
                            </option>
                        </select>
                    </div>
                </div>
            </div>
            <div class="nav-dropdown target-id DeYlt">
                <p id="target_id2">toggle dropdown</p>

                <!-- single-menu dropdown container -->
                <div id="menu_id2" class="menu-id">
                    <div class="form-field full-width">
                        <input
                            type="hidden"
                            name="form_my_contact_form"
                            value="1"
                        />
                        <label for="product_type">Product Type</label>
                        <select
                            name="product_type"
                            id="product_type"
                            required=""
                        >
                            <option value="book">Book</option>
                            <option value="movie">Movie</option>
                            <option value="music">Music</option>
                            <option value="" disabled="">
                                Select a product type
                            </option>
                        </select>
                    </div>
                </div>
            </div>
        </div>
        </header>

        <!-- jquery cdn -->
        <script src="popup/jquery/jquery.min.js"></script>

        <!-- single-menu dropdown script -->
        <script src="single-menu-dropdown.js"></script>

        <!-- nice-select script -->
        <script
            src="https://cdnjs.cloudflare.com/ajax/libs/jquery-nice-select/1.1.0/js/jquery.nice-select.min.js"
            integrity="sha512-NqYds8su6jivy1/WLoW8x1tZMRD7/1ZfhWG/jcRQLOzV1k1rIODCpMgoBnar5QXshKJGV7vi0LXLNXPoFsM5Zg=="
            crossorigin="anonymous"
            referrerpolicy="no-referrer"
        ></script>

        <!-- initialize dropdown and select -->
        <script>
            // Call singleMenu function for each menu
           
        </script>
    </body>
</html>

注意:将以下代码移至 javascript 部分只是为了使代码片段可运行,这不是必需的修改。

 singleMenu("target_id1", "menu_id1", false);
 singleMenu("target_id2", "menu_id2", false);
 $(document).ready(function () {
   // Apply the niceSelect plugin to all select elements
   $("select").niceSelect();
 });