자바 스크립트의 동적 요소에 이벤트 첨부


90

동적으로 생성 된 목록에 html 데이터를 동적으로 삽입하려고하지만 동적으로 생성 된 버튼에 대한 onclick 이벤트를 첨부하려고하면 이벤트가 발생하지 않습니다. 솔루션은 정말 감사하겠습니다.

자바 스크립트 코드 :

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을 어떻게 만들고 있습니까?
jim0thy

1
이벤트 리스너를 연결하려고 할 때 요소가 존재하지 않기 때문에 그렇게 말할 것입니다. - 이것 좀 봐 가지고 learn.jquery.com/events/event-delegation
Craicerjack

1
btnSubmit의 이벤트 리스너로하여 addEventListener를 이동
slebetman

답변:


199

이는 요소가 동적으로 생성되기 때문입니다. 이벤트 를 처리 하려면 이벤트 위임 을 사용해야합니다 .

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

jquery는 다음을 더 쉽게 만듭니다.

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

이벤트 위임 이벤트 위임 기사에 대한 기사입니다.


1
감사합니다! 때로는 순수한 JS 솔루션이 iphone에서 작동하지 않지만 예를 들어 다른 상위 div에서 문서를 변경하면 작동합니다. 위임을위한 최상의 솔루션은 무엇입니까?
Kholiavko

3
@Kholiavko "부모"요소가 동적으로 생성되지 않는 한 작동해야합니다. 이벤트 핸들러를 dynamic create d 요소의 첫 번째 부모에 바인딩하여 다른 이벤트 핸들러간에 충돌이 발생하지 않도록합니다
jilykate

1
그러나 때로는 iPhone에서 작동하지 않으며 "부모"요소도 동적으로 생성되지 않습니다. 다음은 exapmle 이며 모든 브라우저에서 작동하지만 iphone (safary 및 심지어 크롬)에서는 작동하지 않습니다. 그리고 나는 이유를 이해하지 못한다.
Kholiavko

2
@Kholiavko 이것은 codepen이 모든 것을 포장하기 위해 샌드 박스를 사용하기 때문입니다. 정말 정상적인 페이지에 코드를 작성하면 작동합니다
jilykate

2
예상치 못한 유형 강제 변환 방지하기 위해 별도의 등호 (===)를 추가하는 것을 고려
스타카토

17

클릭을 캡처 document.body한 다음 이벤트 대상을 확인 하는 해결 방법이 있습니다 .

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

2
또는 가장 가까운 컨테이너
mplungjan

완벽한 대답!
Rahul Purohit

와우 이것은 놀랍고 많은 시간과 코드를 절약합니다! :)
Anna Medyukh

참고 : event.srcElement는 더 이상 사용되지 않습니다. 대신 event.target을 사용하는 것이 좋습니다.
dsanchez

12

요소를 삽입 한 후에 이벤트를 첨부해야합니다. 예를 들어 글로벌 이벤트를 삽입하지 않고 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>


2
이 접근 방식은 CSR (Client Side Rendering)에서만 작동합니다. SSR (Server-Side Rendering)에서는 작동하지 않습니다. CSR과 SSR 모두에서 작동하도록하려면 이벤트 위임 이 솔루션입니다.javascript document.addEventListener('click',function(e){ if(e.target && e.target.id== 'brnPrepend'){ //do something } });
Yatendra Goel

그러나 성능상의 이유로이 방법이 더 좋습니다. 따라서 사용 사례에 따라 다릅니다.
R3tep

8

차이점은 DOM에서 요소를 만들고 추가하는 방법에 있습니다.

를 통해 요소를 만드는 경우 document.createElement이벤트 리스너를 추가하고 DOM에 추가합니다. 이벤트가 시작됩니다.

다음과 같은 문자열로 요소를 생성하는 경우 : html + = "<li> test </ li>"`, 요소는 기술적으로 문자열입니다. 문자열은 이벤트 리스너를 가질 수 없습니다.

한 가지 해결책은 각 요소를 생성 document.createElement한 다음 DOM 요소에 직접 추가하는 것입니다.

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

3

다음과 유사한 작업을 수행 할 수 있습니다.

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

이제 다음과 같은 이벤트를 등록 할 수 있습니다.

_( 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 이상


2

이를 돕기 위해 작은 라이브러리를 만들었습니다. 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 () 메서드를 사용 하여 지정된 선택기에 대해 대상 요소를 테스트합니다. 이벤트가 트리거되면 대상 요소가 지정된 선택자와 일치하는 경우에만 콜백이 호출됩니다.


0

주제가 너무 오래되었다는 것을 알고 있지만 pure를 사용하여 잘 작동하고 매우 쉽게 작동하는 매우 유용한 코드를 작성하기 위해 몇 분을 주셨습니다 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>


0

이를 위해 간단한 기능을 만들었습니다.

_case함수를 사용하면 대상을 가져올 수있을뿐만 아니라 이벤트를 바인딩하는 부모 요소도 가져올 수 있습니다.

콜백 함수는 대상 ( evt.target) 및 선택자 ( this) 와 일치하는 상위 요소 를 보유하는 이벤트를 반환합니다 . 여기에서 요소를 클릭 한 후 필요한 작업을 수행 할 수 있습니다.

나는 아직 어느 것이 더 나은지 결정하지 않았습니다 if-else.switch

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>

도움이 되었기를 바랍니다.


0

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. 나는 다양한 방법 사이에 실용적인 차이가 있는지 의심합니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.