【问题标题】:Attach event to dynamic elements in javascript将事件附加到javascript中的动态元素
【发布时间】:2020-08-11 01:14:04
【问题描述】:

我正在尝试将 html 数据动态插入到动态创建的列表中,但是当我尝试为动态创建的按钮附加 onclick 事件时,该事件不会触发。非常感谢您的解决方案。

Javascript 代码:

document.addEventListener('DOMContentLoaded', function () {
   document.getElementById('btnSubmit').addEventListener('click', function () {
        var name = document.getElementById('txtName').value;
        var mobile = document.getElementById('txtMobile').value;
        var html = '<ul>';
        for (i = 0; i < 5; i++) {
            html = html + '<li>' + name + i + '</li>';
        }
        html = html + '</ul>';

        html = html + '<input type="button" value="prepend" id="btnPrepend" />';
        document.getElementsByTagName('form')[0].insertAdjacentHTML('afterend', html);
    });

    document.getElementById('btnPrepend').addEventListener('click', function () {
        var html = '<li>Prepending data</li>';
        document.getElementsByTagName('ul')[0].insertAdjacentHTML('afterbegin', html);
    });
});

HTML 代码:

<form>
    <div class="control">
        <label>Name</label>
        <input id="txtName" name="txtName" type="text" />
    </div>
    <div class="control">
        <label>Mobile</label>
        <input id="txtMobile" type="text" />
    </div>
    <div class="control">
        <input id="btnSubmit" type="button" value="submit" />
    </div>
</form>

【问题讨论】:

  • 你是如何创建 html 的?
  • 我会说它是因为当您尝试附加事件侦听器时该元素不存在。 - 看看这个learn.jquery.com/events/event-delegation
  • 将 addEventListener 移动到 btnSubmit 的事件监听器中
  • 嘿,我只是想提一下,您似乎正在尝试创建一个包含 li 元素的 ul 元素。相反,您可以使用 ``(反引号)并按照通常在 HTML 中的方式放置元素。

标签: javascript


【解决方案1】:

这是一个利用element.matches的可重用函数:

function delegate_event(event_type, ancestor_element, target_element_selector, listener_function)
{
    ancestor_element.addEventListener(event_type, function(event)
    {
        if (event.target && event.target.matches && event.target.matches(target_element_selector))
        {
            (listener_function)(event);
        }
    });
}

下面是你如何将它用于点击事件:

delegate_event('click', document, '.alert-button', your_function_here);

【讨论】:

    【解决方案2】:
    First of all add the dynamic class to the dynamically created inputboxes
    var ele = document.createElement('textarea');
    ele.className = "css-class-name"; // set the CSS class
    ele.setAttribute('type', 'textarea');
    ele.setAttribute('value', '');
    ele.setAttribute("id", `row${rcount}_${c}`);
     then do the following 
                const btns = document.querySelectorAll('.css-class-name');
                for (let i = 0; i < btns.length; i++) {
                    btns[i].addEventListener('keyup', function (e) {
                        console.log(e.target.id);
                        let textValues = $(`#${e.target.id}`).val()
                        console.log("=============values =====", textValues)
                        //on key press take id and set value of that id what i am inputting.
                    });
                }
    

    【讨论】:

      【解决方案3】:

      有一种解决方法是捕获对document.body 的点击,然后检查事件目标。

      document.body.addEventListener( 'click', function ( event ) {
        if( event.target.id == 'btnSubmit' ) {
          someFunc();
        };
      } );
      

      【讨论】:

      • 或最近的容器
      • 完美答案!
      • 哇,这太棒了,节省了很多时间和代码! :)
      • 注意:event.srcElement 已被弃用。建议改用 event.target。
      【解决方案4】:

      这是我想出的另一个解决方案。与事件目标相比,我发现这更容易处理,因为如果单击孩子,它们不会触发。方法是设置事件监听器,并在将新元素附加到文档后重置它们:

      function listen() {
          document.querySelectorAll('.btn').forEach(btn => {
              btn.addEventListener("click", function(){
                  console.log("Hello World");
              });
          });
      }
      
      listen();
      
      document.body.innerHTML += "<p class='btn'>Click Here 2.0 .</p>";
      
      listen();
      <!DOCTYPE html>
      <html>
      <body>
      
      <p class='btn'>Click Here.</p>
      
      </body>
      </html>

      编辑: 为避免同一事件的堆叠监听器,请参阅:Answer

      【讨论】:

      • 有 2 个问题,1) 每次必须添加新项目时,您都必须触发 listen() 函数,2) 您每次都将多个 click 类型的事件绑定到上一个运行 listen() 函数时的元素。
      【解决方案5】:

      我发现 jillykate 发布的解决方案有效,但前提是目标元素是最嵌套的。如果不是这种情况,可以通过迭代父母来纠正,即

      function on_window_click(event)
      {
          let e = event.target;
      
          while (e !== null)
          {
              // --- Handle clicks here, e.g. ---
              if (e.getAttribute(`data-say_hello`))
              {
                  console.log("Hello, world!");
              }
      
              e = e.parentElement;
          }
      }
      
      window.addEventListener("click", on_window_click);
      

      还请注意,我们可以通过任何属性处理事件,或者在任何级别附加我们的侦听器。上面的代码使用自定义属性和window。我怀疑各种方法之间是否存在任何实际差异。

      【讨论】:

        【解决方案6】:

        您必须在插入元素之后附加事件,就像您不会在 document 上附加全局事件,而是在插入元素上附加特定事件。

        例如

        document.getElementById('form').addEventListener('submit', function(e) {
          e.preventDefault();
          var name = document.getElementById('txtName').value;
          var idElement = 'btnPrepend';
          var html = `
            <ul>
              <li>${name}</li>
            </ul>
            <input type="button" value="prepend" id="${idElement}" />
          `;
          /* Insert the html into your DOM */
          insertHTML('form', html);
          /* Add an event listener after insert html */
          addEvent(idElement);
        });
        
        const insertHTML = (tag = 'form', html, position = 'afterend', index = 0) => {
          document.getElementsByTagName(tag)[index].insertAdjacentHTML(position, html);
        }
        const addEvent = (id, event = 'click') => {
          document.getElementById(id).addEventListener(event, function() {
            insertHTML('ul', '<li>Prepending data</li>', 'afterbegin')
          });
        }
        <form id="form">
          <div>
            <label for="txtName">Name</label>
            <input id="txtName" name="txtName" type="text" />
          </div>
          <input type="submit" value="submit" />
        </form>

        【讨论】:

        • 此方法仅适用于 CSR(客户端渲染)。这不适用于 SSR(服务器端渲染)。为了使其适用于 CSR 和 SSR,Event Delegation 是解决方案:javascript document.addEventListener('click',function(e){ if(e.target &amp;&amp; e.target.id== 'brnPrepend'){ //do something } });
        • 但出于性能原因,这种方法更好。所以这取决于你的用例。
        【解决方案7】:

        我为此做了一个简单的函数。

        _case 函数不仅可以获取目标,还可以获取绑定事件的父元素。

        回调函数返回包含目标(evt.target)和匹配选择器的父元素(this)的事件。单击元素后,您可以在此处执行所需的操作。

        我还没有决定哪个更好,if-elseswitch

        var _case = function(evt, selector, cb) {
          var _this = evt.target.closest(selector);
          if (_this && _this.nodeType) {
            cb.call(_this, evt);
            return true;
          } else { return false; }
        }
        
        document.getElementById('ifelse').addEventListener('click', function(evt) {
          if (_case(evt, '.parent1', function(evt) {
              console.log('1: ', this, evt.target);
            })) return false;
        
          if (_case(evt, '.parent2', function(evt) {
              console.log('2: ', this, evt.target);
            })) return false;
        
          console.log('ifelse: ', this);
        })
        
        
        document.getElementById('switch').addEventListener('click', function(evt) {
          switch (true) {
            case _case(evt, '.parent3', function(evt) {
              console.log('3: ', this, evt.target);
            }): break;
            case _case(evt, '.parent4', function(evt) {
              console.log('4: ', this, evt.target);
            }): break;
            default:
              console.log('switch: ', this);
              break;
          }
        })
        #ifelse {
          background: red;
          height: 100px;
        }
        #switch {
          background: yellow;
          height: 100px;
        }
        <div id="ifelse">
          <div class="parent1">
            <div class="child1">Click me 1!</div>
          </div>
          <div class="parent2">
            <div class="child2">Click me 2!</div>
          </div>
        </div>
        
        <div id="switch">
          <div class="parent3">
            <div class="child3">Click me 3!</div>
          </div>
          <div class="parent4">
            <div class="child4">Click me 4!</div>
          </div>
        </div>

        希望对你有帮助!

        【讨论】:

          【解决方案8】:

          我知道这个话题太老了,但我给了自己一些时间来创建一个非常有用的代码,使用纯 JAVASCRIPT 可以很好地工作并且非常容易。 这是带有一个简单示例的代码:

          String.prototype.addEventListener=function(eventHandler, functionToDo){
            let selector=this;
            document.body.addEventListener(eventHandler, function(e){
              e=(e||window.event);
              e.preventDefault();
              const path=e.path;
              path.forEach(function(elem){
                const selectorsArray=document.querySelectorAll(selector);
                selectorsArray.forEach(function(slt){
                  if(slt==elem){
                    if(typeof functionToDo=="function") functionToDo(el=slt, e=e);
                  }
                });
              });
            });
          }
          
          // And here is how we can use it actually !
          
          "input[type='number']".addEventListener("click", function(element, e){
          	console.log( e ); // Console log the value of the current number input
          });
          <input type="number" value="25">
          <br>
          <input type="number" value="15">
          <br><br>
          <button onclick="addDynamicInput()">Add a Dynamic Input</button>
          <script type="text/javascript">
            function addDynamicInput(){
              const inpt=document.createElement("input");
                    inpt.type="number";
                    inpt.value=Math.floor(Math.random()*30+1);
              document.body.prepend(inpt);
            }
          </script>

          【讨论】:

            【解决方案9】:

            不同之处在于您在 DOM 中创建和附加元素的方式。

            如果您通过document.createElement 创建元素,请添加事件侦听器,并将其附加到 DOM。您的事件将触发。

            如果您将元素创建为这样的字符串:html += "

          • test
          • "`,从技术上讲,该元素只是一个字符串。字符串不能有事件监听器。

            一种解决方案是使用 document.createElement 创建每个元素,然后直接将它们添加到 DOM 元素中。

            // Sample
            let li = document.createElement('li')
            document.querySelector('ul').appendChild(li)
            

            【讨论】:

              【解决方案10】:

              这是因为您的元素是动态创建的。您应该使用event delegation 来处理该事件。

               document.addEventListener('click',function(e){
                  if(e.target && e.target.id== 'brnPrepend'){
                        //do something
                   }
               });
              

              jquery 让它更容易:

               $(document).on('click','#btnPrepend',function(){//do something})
              

              这是一篇关于事件委托的文章event delegation article

              【讨论】:

              • 谢谢!有时纯 JS 解决方案在 iphone 上不起作用,但如果在另一个父 div 上更改文档,例如,它可以工作。委托的最佳解决方案是什么?
              • @Kholiavko 我想说,只要“父”元素不是动态创建的,它就可以工作。我会将事件处理程序绑定到动态 create d 元素的第一个父级,这样不同的事件处理程序之间就不会发生冲突
              • 但有时它在 iphone 上不起作用,甚至“父”元素也不是动态创建的。这是exapmle,它适用于所有浏览器,但不适用于iphone(safary 甚至chrome)。我不明白为什么。
              • @Kholiavko 这只是因为 codepen 使用沙箱来包装所有内容。如果您真的在普通页面中编写这些代码,它将起作用
              • 考虑添加一个额外的等号 (===) 以防止意外的类型强制
              【解决方案11】:

              我创建了一个小型库来帮助解决这个问题:Library source on GitHub

              <script src="dynamicListener.min.js"></script>
              <script>
              // Any `li` or element with class `.myClass` will trigger the callback, 
              // even elements created dynamically after the event listener was created.
              addDynamicEventListener(document.body, 'click', '.myClass, li', function (e) {
                  console.log('Clicked', e.target.innerText);
              });
              </script>
              

              功能类似于jQuery.on()。

              库使用Element.matches() 方法针对给定的选择器测试目标元素。触发事件时,仅当目标元素与给定的选择器匹配时才会调用回调。

              【讨论】:

                【解决方案12】:
                var __ = function(){
                    this.context  = [];
                    var self = this;
                    this.selector = function( _elem, _sel ){
                        return _elem.querySelectorAll( _sel );
                    }
                          this.on = function( _event, _element, _function ){
                              this.context = self.selector( document, _element );
                              document.addEventListener( _event, function(e){
                                  var elem = e.target;
                                  while ( elem != null ) {
                                      if( "#"+elem.id == _element || self.isClass( elem, _element ) || self.elemEqal( elem ) ){
                                          _function( e, elem );
                                      }
                                      elem = elem.parentElement;
                                  }
                              }, false );
                     };
                
                     this.isClass = function( _elem, _class ){
                        var names = _elem.className.trim().split(" ");
                        for( this.it = 0; this.it < names.length; this.it++ ){
                            names[this.it] = "."+names[this.it];
                        }
                        return names.indexOf( _class ) != -1 ? true : false;
                    };
                
                    this.elemEqal = function( _elem ){
                        var flg = false;
                        for( this.it = 0; this.it < this.context.length;  this.it++ ){
                            if( this.context[this.it] === _elem && !flg ){
                                flg = true;
                            }
                        }
                        return flg;
                    };
                
                }
                
                    function _( _sel_string ){
                        var new_selc = new __( _sel_string );
                        return new_selc;
                    }
                

                现在您可以像这样注册事件了,

                _( document ).on( "click", "#brnPrepend", function( _event, _element ){
                      console.log( _event );
                      console.log( _element );
                      // Todo
                
                  });
                

                浏览器支持

                chrome - 4.0、Edge - 9.0、Firefox - 3.5 Safari - 3.2、Opera - 10.0 及更高版本

                【讨论】:

                  【解决方案13】:

                  你可以做类似这样的事情:

                  // Get the parent to attatch the element into
                  var parent = document.getElementsByTagName("ul")[0];
                  
                  // Create element with random id
                  var element = document.createElement("li");
                  element.id = "li-"+Math.floor(Math.random()*9999);
                  
                  // Add event listener
                  element.addEventListener("click", EVENT_FN);
                  
                  // Add to parent
                  parent.appendChild(element);
                  

                  【讨论】:

                    猜你喜欢
                    • 1970-01-01
                    • 1970-01-01
                    • 2021-10-20
                    • 2016-07-29
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    相关资源
                    最近更新 更多