【问题标题】:Event binding on dynamically created elements?动态创建元素的事件绑定?
【发布时间】:2020-09-29 05:46:11
【问题描述】:

我有一些代码循环浏览页面上的所有选择框并将.hover 事件绑定到它们以在mouse on/off 上调整它们的宽度。

这发生在页面准备就绪并且工作正常。

我遇到的问题是我在初始循环之后通过 Ajax 或 DOM 添加的任何选择框都不会绑定事件。

我找到了这个插件 (jQuery Live Query Plugin),但是在我使用插件向我的页面添加另一个 5k 之前,我想看看是否有人知道这样做的方法,无论是直接使用 jQuery 还是通过其他选项。

【问题讨论】:

    标签: javascript jquery events unobtrusive-javascript


    【解决方案1】:

    从 jQuery 1.7 开始您应该使用 jQuery.fn.on 并填充选择器参数:

    $(staticAncestors).on(eventName, dynamicChild, function() {});
    

    解释:

    这称为事件委托,其工作方式如下。该事件附加到应处理的元素的静态父级 (staticAncestors)。每次在此元素或其中一个后代元素上触发事件时,都会触发此 jQuery 处理程序。然后,处理程序检查触发事件的元素是否与您的选择器匹配 (dynamicChild)。当匹配时,您的自定义处理函数就会被执行。


    在此之前,推荐的方法是使用live()

    $(selector).live( eventName, function(){} );
    

    但是,live() 在 1.7 中被弃用,取而代之的是 on(),并在 1.9 中完全删除。 live()签名:

    $(selector).live( eventName, function(){} );
    

    ...可以替换为以下on()签名:

    $(document).on( eventName, selector, function(){} );
    

    例如,如果您的页面正在动态创建类名称为 dosomething 的元素,您可以将事件绑定到 已存在的父级(这是问题的关键,您需要存在要绑定的东西,不要绑定到动态内容),这可以(也是最简单的选择)是document。虽然记住document may not be the most efficient option

    $(document).on('mouseover mouseout', '.dosomething', function(){
        // what you want to happen when mouseover and mouseout 
        // occurs on elements that match '.dosomething'
    });
    

    绑定事件时存在的任何父级都可以。例如

    $('.buttons').on('click', 'button', function(){
        // do something here
    });
    

    适用于

    <div class="buttons">
        <!-- <button>s that are generated dynamically and added here -->
    </div>
    

    【讨论】:

    • 在此处了解有关事件委托的更多信息:learn.jquery.com/events/event-delegation
    • 该解决方案仍然会在 jQuery 3 和 firefox 中带来错误,因为在清除 html 并重新创建它之后,事件会被触发两次,
    • 虽然此解决方案至少十次提到“事件委托”,但它实际上并没有向您展示如何将事件委托给动态绑定的函数。
    【解决方案2】:

    jQuery.fn.on的文档中有很好的解释。

    简而言之:

    事件处理程序仅绑定到当前选定的元素;当您的代码调用 .on() 时,它们必须存在于页面上。

    因此在以下示例中,#dataTable tbody tr 必须在生成代码之前存在。

    $("#dataTable tbody tr").on("click", function(event){
        console.log($(this).text());
    });
    

    如果新的 HTML 被注入到页面中,最好使用委托事件来附加事件处理程序,如下所述。

    委托事件的优点是它们可以处理来自以后添加到文档的后代元素的事件。例如,如果表存在,但行是使用代码动态添加的,则以下将对其进行处理:

    $("#dataTable tbody").on("click", "tr", function(event){
        console.log($(this).text());
    });
    

    除了能够处理尚未创建的后代元素上的事件外,委托事件的另一个优点是当必须监视许多元素时,它们的开销可能会大大降低。在tbody 中包含 1,000 行的数据表中,第一个代码示例将处理程序附加到 1,000 个元素。

    委托事件方法(第二个代码示例)仅将事件处理程序附加到一个元素 tbody,并且事件只需要冒泡一个级别(从单击的 trtbody) .

    注意:委派事件不适用于SVG

    【讨论】:

      【解决方案3】:

      这是一个纯 JavaScript 解决方案,没有任何库或插件:

      document.addEventListener('click', function (e) {
          if (hasClass(e.target, 'bu')) {
              // .bu clicked
              // Do your thing
          } else if (hasClass(e.target, 'test')) {
              // .test clicked
              // Do your other thing
          }
      }, false);
      

      hasClass 在哪里

      function hasClass(elem, className) {
          return elem.className.split(' ').indexOf(className) > -1;
      }
      

      Live demo

      感谢 Dave 和 Sime Vidas

      使用更现代的 JS,hasClass 可以实现为:

      function hasClass(elem, className) {
          return elem.classList.contains(className);
      }
      

      下面嵌入了相同的 jsfiddle Live 演示:

      function hasClass(elem, className) {
        return elem.classList.contains(className);
      }
      
      document.addEventListener('click', function(e) {
        if (hasClass(e.target, 'bu')) {
          alert('bu');
          document.querySelector('.bu').innerHTML = '<div class="bu">Bu<div class="tu">Tu</div></div>';
        } else if (hasClass(e.target, 'test')) {
          alert('test');
        } else if (hasClass(e.target, 'tu')) {
          alert('tu');
        }
      
      }, false);
      .test,
      .bu,
      .tu {
        border: 1px solid gray;
        padding: 10px;
        margin: 10px;
      }
      <div class="test">Test
        <div class="bu">Bu</div>test
      </div>

      【讨论】:

      【解决方案4】:

      您可以在创建对象时为其添加事件。如果您在不同时间将相同的事件添加到多个对象,则创建一个命名函数可能是可行的方法。

      var mouseOverHandler = function() {
          // Do stuff
      };
      var mouseOutHandler = function () {
          // Do stuff
      };
      
      $(function() {
          // On the document load, apply to existing elements
          $('select').hover(mouseOverHandler, mouseOutHandler);
      });
      
      // This next part would be in the callback from your Ajax call
      $("<select></select>")
          .append( /* Your <option>s */ )
          .hover(mouseOverHandler, mouseOutHandler)
          .appendTo( /* Wherever you need the select box */ )
      ;
      

      【讨论】:

        【解决方案5】:

        您可以简单地将事件绑定调用包装到一个函数中,然后调用它两次:一次在文档准备好时,一次在添加新 DOM 元素的事件之后。如果您这样做,您将希望避免在现有元素上绑定相同的事件两次,因此您需要取消绑定现有事件或(更好)仅绑定到新创建的 DOM 元素。代码看起来像这样:

        function addCallbacks(eles){
            eles.hover(function(){alert("gotcha!")});
        }
        
        $(document).ready(function(){
            addCallbacks($(".myEles"))
        });
        
        // ... add elements ...
        addCallbacks($(".myNewElements"))
        

        【讨论】:

        • 这篇文章确实帮助我了解了我在加载相同的表单并获得 1,2,4,8,16... 提交时遇到的问题。而不是使用 .live() 我只是在我的 .load() 回调中使用了 .bind() 。问题解决了。谢谢!
        【解决方案6】:

        尝试使用.live() 而不是.bind().live() 将在 Ajax 请求执行后将 .hover 绑定到您的复选框。

        【讨论】:

        • 方法 live() 在 1.7 版中被弃用,取而代之的是 on,并在 1.9 版中被删除。
        【解决方案7】:

        动态创建元素的事件绑定

        单元素:

        $(document.body).on('click','.element', function(e) {  });
        

        子元素:

         $(document.body).on('click','.element *', function(e) {  });
        

        注意添加的*。将为该元素的所有子元素触发一个事件。

        我注意到:

        $(document.body).on('click','.#element_id > element', function(e) {  });
        

        它不再工作了,但它以前工作过。我一直在使用来自 Google CDN 的 jQuery,但我不知道他们是否更改了它。

        【讨论】:

        • 是的,他们并没有说(document.body)它说祖先几乎可以是任何东西
        【解决方案8】:

        这是由事件委托完成的。事件将绑定到 wrapper-class 元素,但将委托给 selector-class 元素。这就是它的工作原理。

        $('.wrapper-class').on("click", '.selector-class', function() {
            // Your code here
        });
        

        HTML

        <div class="wrapper-class">
            <button class="selector-class">
              Click Me!
            </button>
        </div>    
        

        #注意: wrapper-class 元素可以是任何东西。文档、正文或您的包装。 包装器应该已经存在。但是,selector 不一定需要在页面加载时显示。它可能会稍后出现,并且事件将绑定到selector不会失败

        【讨论】:

        • 是的,但它会为元素创建事件传播。您需要添加目标特定的动态元素并停止事件传播。
        • 如果您在父元素和后代元素上绑定相同的事件,则可以使用 event.stopPropagation() 停止它
        【解决方案9】:

        我更喜欢使用选择器并将其应用于文档。

        这将自身绑定在文档上,并将适用于页面加载后将呈现的元素。

        例如:

        $(document).on("click", 'selector', function() {
            // Your code here
        });
        

        【讨论】:

        • 您应该将其绑定到最近的静态父级而不是整个文档。
        【解决方案10】:

        您可以使用 live() 方法将元素(甚至是新创建的元素)绑定到事件和处理程序,例如 onclick 事件。

        这是我编写的示例代码,您可以在其中看到 live() 方法如何将所选元素(甚至是新创建的元素)绑定到事件:

        <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
        <html xmlns="http://www.w3.org/1999/xhtml">
            <head>
                <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
                <title>Untitled Document</title>
            </head>
        
            <body>
                <script src="http://code.jquery.com/jquery-latest.js"></script>
                <script src="http://ajax.aspnetcdn.com/ajax/jquery.ui/1.8.16/jquery-ui.min.js"></script>
        
                <input type="button" id="theButton" value="Click" />
                <script type="text/javascript">
                    $(document).ready(function()
                        {
                            $('.FOO').live("click", function (){alert("It Works!")});
                            var $dialog = $('<div></div>').html('<div id="container"><input type ="button" id="CUSTOM" value="click"/>This dialog will show every time!</div>').dialog({
                                                                                                                 autoOpen: false,
                                                                                                                 tite: 'Basic Dialog'
                                                                                                             });
                            $('#theButton').click(function()
                            {
                                $dialog.dialog('open');
                                return('false');
                            });
                            $('#CUSTOM').click(function(){
                                //$('#container').append('<input type="button" value="clickmee" class="FOO" /></br>');
                                var button = document.createElement("input");
                                button.setAttribute('class','FOO');
                                button.setAttribute('type','button');
                                button.setAttribute('value','CLICKMEE');
                                $('#container').append(button);
                            });
                            /* $('#FOO').click(function(){
                                                             alert("It Works!");
                                                         }); */
                    });
                </script>
            </body>
        </html>
        

        【讨论】:

          【解决方案11】:

          另一种解决方案是在创建元素时添加监听器。不是将侦听器放在主体中,而是在创建它的那一刻将侦听器放在元素中:

          var myElement = $('<button/>', {
              text: 'Go to Google!'
          });
          
          myElement.bind( 'click', goToGoogle);
          myElement.append('body');
          
          
          function goToGoogle(event){
              window.location.replace("http://www.google.com");
          }
          

          【讨论】:

          • 您的代码包含 1 个错误:myElement.append('body'); 必须是 myElement.appendTo('body');。另一方面,如果不需要进一步使用变量myElement,这样会更容易更短:$('body').append($('&lt;button/&gt;', { text: 'Go to Google!' }).bind( 'click', goToGoogle));
          【解决方案12】:

          这样试试——

          $(document).on( 'click', '.click-activity', function () { ... });
          

          【讨论】:

          • 您应该将其绑定到最近的静态父级而不是整个文档。
          【解决方案13】:

          注意放置元素的“MAIN”类,例如,

          <div class="container">
               <ul class="select">
                   <li> First</li>
                   <li>Second</li>
              </ul>
          </div>
          

          在上述场景中,jQuery 将监视的 MAIN 对象是“容器”。

          那么你在容器下基本上会有ulliselect等元素名称:

          $(document).ready(function(e) {
              $('.container').on( 'click',".select", function(e) {
                  alert("CLICKED");
              });
           });
          

          【讨论】:

            【解决方案14】:

            您可以在使用jQuery(html, attributes) 动态创建时将事件附加到元素。

            从 jQuery 1.8 开始,任何 jQuery 实例方法(jQuery.fn 的方法)都可以用作传递给 第二个参数:

            function handleDynamicElementEvent(event) {
              console.log(event.type, this.value)
            }
            // create and attach event to dynamic element
            jQuery("<select>", {
                html: $.map(Array(3), function(_, index) {
                  return new Option(index, index)
                }),
                on: {
                  change: handleDynamicElementEvent
                }
              })
              .appendTo("body");
            <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js">
            </script>

            【讨论】:

              【解决方案15】:

              你可以使用

              $('.buttons').on('click', 'button', function(){
                  // your magic goes here
              });
              

              $('.buttons').delegate('button', 'click', function() {
                  // your magic goes here
              });
              

              这两种方法是等价的,但参数的顺序不同。

              见:jQuery Delegate Event

              【讨论】:

              • delegate() 现在已弃用。不要使用它。
              【解决方案16】:

              这就是动态创建的元素不响应点击的原因:

              var body = $("body");
              var btns = $("button");
              var btnB = $("<button>B</button>");
              // `<button>B</button>` is not yet in the document.
              // Thus, `$("button")` gives `[<button>A</button>]`.
              // Only `<button>A</button>` gets a click listener.
              btns.on("click", function () {
                console.log(this);
              });
              // Too late for `<button>B</button>`...
              body.append(btnB);
              <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
              <button>A</button>

              作为一种解决方法,您必须听取所有点击并检查源元素:

              var body = $("body");
              var btnB = $("<button>B</button>");
              var btnC = $("<button>C</button>");
              // Listen to all clicks and
              // check if the source element
              // is a `<button></button>`.
              body.on("click", function (ev) {
                if ($(ev.target).is("button")) {
                  console.log(ev.target);
                }
              });
              // Now you can add any number
              // of `<button></button>`.
              body.append(btnB);
              body.append(btnC);
              <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
              <button>A</button>

              这称为“事件委托”。好消息,它是 jQuery 的内置功能 :-)

              var i = 11;
              var body = $("body");
              body.on("click", "button", function () {
                var letter = (i++).toString(36).toUpperCase();
                body.append($("<button>" + letter + "</button>"));
              });
              <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
              <button>A</button>

              【讨论】:

              • 也可以直接赋值var body = $("body").on( ... );
              【解决方案17】:

              在绑定事件时不存在的任何 p不存在,并且如果您的页面 动态创建元素 具有类名称 button 您会将事件绑定到已经存在的父级

              $(document).ready(function(){
                //Particular Parent chield click
                $(".buttons").on("click","button",function(){
                  alert("Clicked");
                });  
                
                //Dynamic event bind on button class  
                $(document).on("click",".button",function(){
                  alert("Dymamic Clicked");
                });
                $("input").addClass("button");  
              });
              <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
              <div class="buttons">
                <input type="button" value="1">
                <button>2</button>
                <input type="text">
                <button>3</button>  
                <input type="button" value="5">  
                </div>
              <button>6</button>

              【讨论】:

                【解决方案18】:

                将事件绑定到已经存在的父级:

                $(document).on("click", "selector", function() {
                    // Your code here
                });
                

                【讨论】:

                  【解决方案19】:

                  另一种创建元素和绑定事件的灵活解决方案 (source)

                  // creating a dynamic element (container div)
                  var $div = $("<div>", {id: 'myid1', class: 'myclass'});
                  
                  //creating a dynamic button
                   var $btn = $("<button>", { type: 'button', text: 'Click me', class: 'btn' });
                  
                  // binding the event
                   $btn.click(function () { //for mouseover--> $btn.on('mouseover', function () {
                      console.log('clicked');
                   });
                  
                  // append dynamic button to the dynamic container
                  $div.append($btn);
                  
                  // add the dynamically created element(s) to a static element
                  $("#box").append($div);
                  

                  注意:这将为每个元素创建一个事件处理程序实例(在循环中使用时可能会影响性能)

                  【讨论】:

                    【解决方案20】:

                    使用 jQuery http://api.jquery.com/on/.on() 方法将事件处理程序附加到活动元素。

                    从 1.9 版开始,.live() 方法也被删除。

                    【讨论】:

                      【解决方案21】:

                      我更喜欢以模块化函数方式部署事件侦听器,而不是编写document 级别事件侦听器的脚本。所以,我喜欢下面。 注意,你不能用同一个事件监听器过度订阅一个元素,所以不必担心不止一次附加一个监听器——只有一个会粘住。

                      var iterations = 4;
                      var button;
                      var body = document.querySelector("body");
                      
                      for (var i = 0; i < iterations; i++) {
                          button = document.createElement("button");
                          button.classList.add("my-button");
                          button.appendChild(document.createTextNode(i));
                          button.addEventListener("click", myButtonWasClicked);
                          body.appendChild(button);
                      }
                      
                      function myButtonWasClicked(e) {
                          console.log(e.target); //access to this specific button
                      }

                      【讨论】:

                      • 我更喜欢这个实现;我只需要设置回电
                      【解决方案22】:
                      <html>
                          <head>
                              <title>HTML Document</title>
                              <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/1.12.0/jquery.min.js"></script>
                          </head>
                      
                          <body>
                              <div id="hover-id">
                                  Hello World
                              </div>
                      
                              <script>
                                  jQuery(document).ready(function($){
                                      $(document).on('mouseover', '#hover-id', function(){
                                          $(this).css('color','yellowgreen');
                                      });
                      
                                      $(document).on('mouseout', '#hover-id', function(){
                                          $(this).css('color','black');
                                      });
                                  });
                              </script>
                          </body>
                      </html>
                      

                      【讨论】:

                      • 虽然这段代码 sn-p 可以解决问题,但它没有解释为什么或如何回答这个问题。请包括对您的代码的解释,因为这确实有助于提高您的帖子质量。请记住,您是在为将来的读者回答问题,而这些人可能不知道您提出代码建议的原因。
                      【解决方案23】:

                      我正在寻找一种解决方案,让 $.bind$.unbind 在动态添加的元素中正常工作。

                      由于on() 制造了附加事件的技巧,以便对我来到的那些人创建一个虚假的解除绑定:

                      const sendAction = function(e){ ... }
                      // bind the click
                      $('body').on('click', 'button.send', sendAction );
                      
                      // unbind the click
                      $('body').on('click', 'button.send', function(){} );
                      

                      【讨论】:

                      • 解除绑定不起作用,这只是添加了另一个指向空函数的事件...
                      猜你喜欢
                      相关资源
                      最近更新 更多