Đính kèm sự kiện vào các phần tử động trong javascript


90

Tôi đang cố gắng chèn động dữ liệu html vào danh sách được tạo động, nhưng khi tôi cố gắng đính kèm sự kiện onclick cho nút được tạo động, sự kiện không kích hoạt. Giải pháp sẽ thực sự được đánh giá cao.

Mã 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);
});

});

Mã 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>

Bạn đang tạo html như thế nào?
jim0thy

1
Tôi sẽ nói điều đó vì phần tử không tồn tại khi bạn cố gắng đính kèm trình nghe sự kiện. - có một cái nhìn tại này learn.jquery.com/events/event-delegation
Craicerjack

1
Di chuyển addEventListener của bạn vào event listener của btnSubmit
slebetman

Câu trả lời:


199

Điều này là do phần tử của bạn được tạo động. Bạn nên sử dụng ủy quyền sự kiện để xử lý sự kiện.

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

jquery làm cho nó dễ dàng hơn:

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

Đây là một bài viết về sự kiện đoàn sự kiện bài báo


1
Cảm ơn bạn! Đôi khi giải pháp JS thuần túy không hoạt động trên iphone, nhưng nếu thay đổi tài liệu trên một số div cha khác, thì nó hoạt động. Giải pháp tốt nhất cho ủy quyền là gì?
Kholiavko

3
@Kholiavko Tôi sẽ nói miễn là phần tử "cha mẹ" không được tạo động, nó sẽ hoạt động. Tôi sẽ ràng buộc xử lý sự kiện cho phụ huynh đầu tiên của động tạo ra nguyên tố d sao cho không có mâu thuẫn giữa xử lý sự kiện khác nhau
jilykate

1
Nhưng đôi khi nó không hoạt động trên iphone, ngay cả phần tử "cha" cũng không được tạo động. Đây là exapmle , nó hoạt động trên tất cả các trình duyệt, nhưng không hoạt động trên iphone (safary và thậm chí cả chrome). Và tôi không hiểu tại sao.
Kholiavko

2
@Kholiavko đây chỉ là vì codepen sử dụng hộp cát để bọc mọi thứ. Nếu bạn thực sự viết những đoạn mã đó trong một trang bình thường, nó sẽ hoạt động
jilykate

2
Hãy xem xét thêm một dấu bằng thêm (===) để ngăn chặn bất ngờ kiểu ép buộc
Staccato

17

Có một cách giải quyết bằng cách nắm bắt các nhấp chuột vào document.bodyvà sau đó kiểm tra mục tiêu sự kiện.

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

2
Hoặc gần thùng chứa
mplungjan

Câu trả lời hoàn hảo!
Rahul Purohit

WOW điều này thật tuyệt vời, tiết kiệm rất nhiều thời gian và mã! :)
Anna Medyukh

Lưu ý: event.srcElement không được dùng nữa. Bạn nên sử dụng event.target để thay thế.
dsanchez

12

Bạn phải đính kèm sự kiện sau khi chèn các phần tử, giống như bạn không đính kèm một sự kiện toàn cục vào sự kiện của bạn documentmà là một sự kiện cụ thể trên các phần tử được chèn.

ví dụ

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>


2
Cách tiếp cận này sẽ chỉ hoạt động đối với CSR (Kết xuất phía máy khách). Điều này sẽ không hoạt động đối với SSR (Kết xuất phía máy chủ). Để làm cho nó hoạt động cho cả CSR và SSR, Ủy quyền sự kiện là giải pháp:javascript document.addEventListener('click',function(e){ if(e.target && e.target.id== 'brnPrepend'){ //do something } });
Yatendra Goel

Nhưng vì lý do hiệu suất, cách tiếp cận này tốt hơn. Vì vậy, nó phụ thuộc vào trường hợp sử dụng của bạn.
R3 bước

8

Sự khác biệt là ở cách bạn tạo và nối các phần tử trong DOM.

Nếu bạn tạo một phần tử thông qua document.createElement, hãy thêm trình xử lý sự kiện và nối phần tử đó vào DOM. Các sự kiện của bạn sẽ bắt đầu.

Nếu bạn tạo một phần tử dưới dạng một chuỗi như sau: html + = "<li> test </li>" `, về mặt kỹ thuật, phần tử chỉ là một chuỗi. Chuỗi không thể có trình nghe sự kiện.

Một giải pháp là tạo từng phần tử với document.createElementvà sau đó thêm chúng trực tiếp vào một phần tử DOM.

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

3

Bạn có thể làm điều gì đó tương tự như sau:

// 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);

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

Bây giờ bạn có thể đăng ký sự kiện như,

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

  });

Hỗ trợ trình duyệt

chrome - 4.0, Edge - 9.0, Firefox - 3.5 Safari - 3.2, Opera - 10.0 trở lên


2

Tôi đã tạo một thư viện nhỏ để trợ giúp việc này: Nguồn thư viện trên 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>

Chức năng tương tự như jQuery.on ().

Thư viện sử dụng phương thức Element.matches () để kiểm tra phần tử mục tiêu với bộ chọn đã cho. Khi một sự kiện được kích hoạt, lệnh gọi lại chỉ được gọi nếu phần tử đích khớp với bộ chọn đã cho.


0

Tôi biết rằng chủ đề này đã quá cũ nhưng tôi đã dành cho mình vài phút để tạo ra một mã rất hữu ích, hoạt động tốt và rất dễ sử dụng thuần túy JAVASCRIPT. Đây là mã với một ví dụ đơn giản:

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>


0

Tôi đã tạo một chức năng đơn giản cho việc này.

Các _casechức năng cho phép bạn không chỉ nhận được các mục tiêu, nhưng cũng có được các yếu tố cha mẹ, nơi bạn có ràng buộc sự kiện trên.

Hàm gọi lại trả về sự kiện chứa target ( evt.target) và phần tử mẹ khớp với selector ( this). Tại đây bạn có thể thực hiện những thứ mình cần sau khi phần tử được nhấp vào.

Tôi đã chưa được quyết định đó là tốt hơn, if-elsehoặcswitch

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>

Hy vọng nó giúp!


0

Tôi đã tìm thấy giải pháp được đăng bởi jillykate hoạt động, nhưng chỉ khi phần tử đích được lồng ghép nhiều nhất. Nếu đây không phải là trường hợp, điều này có thể được sửa chữa bằng cách lặp lại trên cha mẹ, tức là

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);

Cũng lưu ý rằng chúng tôi có thể xử lý các sự kiện theo bất kỳ thuộc tính nào hoặc đính kèm trình lắng nghe của chúng tôi ở bất kỳ cấp độ nào. Đoạn mã trên sử dụng thuộc tính tùy chỉnh và window. Tôi nghi ngờ có bất kỳ sự khác biệt thực dụng nào giữa các phương pháp khác nhau.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.