在动态创建的元素上附加JavaScript/jQuery事件

5

我看过很多类似的问题,但它们通常都针对特定选择器。如果我错过了重复的地方,请告诉我。

假设你想为WordPress、Drupal或基本上任何使用第三方功能的网站构建一个jQuery插件。由于事先不知道需要定位哪些特定事件和选择器,你该如何向动态创建的元素添加jQuery/JavaScript功能呢?此外,你也不知道元素是如何创建的(使用ajax/php等回调)。

例如:

最近我尝试将select2或selectize添加到我的整个站点。但是,由于我不能事先确定其他插件/模块中何时会添加select元素,因此无法确保新创建的selects能正常使用jQuery。

Select2

这是正常的方法,但无法用于动态创建或克隆的selects:

<script type='text/javascript'>
    jQuery(document).ready(function($) {
        $("select").select2();
    });
</script>

如果我知道所有添加这些选择器的事件,我只需要执行以下操作:

 $( ".some_button_that_creates_new_selects" ).on( "click", function() {
        jQuery("select").select2(); 
    });
 });

然而,由于我事先不知道所有动态选择器(因为许多是由第三方插件添加的),因此select2无法在新的选择器上工作。所以上述方法只适用于每种情况。
我需要找到一种遍历dom树并识别新选择器创建的方法。
我尝试使用.on针对几个事件进行定位,但效果很差。
我了解到javascript中的document.getElementsByTagName可以识别dom中的更改,并反映在集合中。我还研究了querySelectorAll,但不确定它的确切工作方式。
我还尝试过使用MutationObserver并检查选择标记的addednodes.length是否发生变化,但没有取得实质性进展。
更新:
经过一些测试,我意识到问题可能在于select2插件本身。Select2加载动态选择器但无法获取其位置。Chrome最终会抛出类型错误:无法读取null的属性“find”,无法读取null的属性“hasclass”。
例如将select2插件添加到wordpress中。单击快速编辑会添加新字段。
新选择器无法正确加载,并且单击它们时没有下拉列表。
单击选择器后再单击其下方将错误地加载select2覆盖层。
2个回答

6

解决方案:

我将回答您的基本问题:“在动态创建的元素上附加javscript/jquery事件”。

要在动态创建的元素上添加事件,您需要利用事件委托,当事件触发在一个元素上时,它会向上传播。

因此,如果您在根元素(可以是body或任何在附加此事件时存在的父元素)上附加事件处理程序

$("body").on("click","elementSelector", function(){

});

现在,这将从动态创建的元素中捕获事件。

应该提到我已经尝试过了,但它并没有起作用。我认为在我的当前情况下,这可能是特定插件select2的问题。 - Bryan Willis
动态创建的下拉选择框看起来像是Select2的选择框,但当你点击它们时它们会被冻结。在分析Chrome中生成的源代码后,我注意到下拉菜单实际上是被添加了,但它们的定位是错误的。Select2似乎在你实际点击每个选择框时添加了jQuery选择框,并使用绝对定位和top、left和css属性将该下拉菜单叠加在普通选择框上。当动态选择框被创建时,它们的定位没有被识别。 - Bryan Willis
如果您查看生成的源代码中的select2下拉菜单,样式生成的源代码为style="display: block; left: 0px; width: 0px; top: 0px; bottom: auto;,而通常它应该看起来像这样style="display: block; left: 453.2528381347656px; width: 149px; top: 150.23011016845703px; bottom: auto;。但是,如果您单击“应该”出现下拉菜单的位置,它会尝试找到其定位,但会出错并将其加载到右侧。 - Bryan Willis

2

最简单的解决方案是在javascript中创建一个函数,每当您在页面上动态添加任何控件时都会调用该函数,在该函数中,您可以放置检查,以便仅在新组件上注册事件。例如:

function registerEvents()
{
    //Get all the select events from the page and register events only on those controls which are new ..
    $("select").each(function() {

        if (!$(this).hasClass("registeredEvent")) {

              $(this).addClass("registeredEvent");
              $(this).select2();
        }
    });

}

通过不同的方法可以获得相同的结果。

同时,在 jQuery 中,也可以通过检查 .live 事件来实现自动注册新元素的事件,只要它们具有相同的标签或类名。


live方法在几个版本前已经被弃用,不应再使用。 - Sanjeev
是的,您说得对。这就是我建议使用自定义函数来处理这些情况的原因。 - razahassank
如果您知道新元素何时被添加,例如第三方提供了一些回调函数,那么您可以通过像razahassank上面提到的函数,在新创建的选择元素上初始化select2插件。 - Sanjeev
就弃用的.live而言,它不是被像我在示例中使用的.on所取代了吗? - Bryan Willis
我喜欢这个例子,但它似乎没有将registeredEvent添加到任何选择器中...也许我漏掉了什么...另外,请查看我的更新以获取我的特定情况。我认为这种情况可能与select2存在问题有关。 - Bryan Willis
请问您能否分享URL,以便我们可以轻松调试? - razahassank

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接