innerHTML 내에서 클릭 이벤트를 얻으려면 어떻게해야합니까?


12

이 레이아웃은 동적으로 만들어졌습니다.

for (let i = 1; i < 10; i++) {
  document.querySelector('.card-body').innerHTML += `<div class="row" id="img_div">
        <div class="col-12 col-sm-12 col-md-2 text-center">
          <img src="http://placehold.it/120x80" alt="prewiew" width="120" height="80">
        </div>
        <div id="text_div" class="col-12 text-sm-center col-sm-12 text-md-left col-md-6">
        <h4 class="name"><a href="#" id="title` + i + `">Name</a></h4>
          <h4>
            <small>state</small>
          </h4>
          <h4>
            <small>city</small>
          </h4>
          <h4>
            <small>zip</small>
          </h4>
        </div>
        <div class="col-12 col-sm-12 text-sm-center col-md-4 text-md-right row">
        </div>
      </div>
     `
  document.getElementById("title" + i).addEventListener('click', function() {
    console.log(i)
  });
}
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous">
<div class="card-body">
  <!-- person -->
</div>

그리고 각 이벤트를 클릭하고 관련 h4 class="name"번호가있는 로그를 표시 하고 싶습니다 i.

그러나 console.log마지막 i관련 항목 만 표시하고 ( i=9이 경우) 다른 i숫자 와는 작동하지 않습니다 . 왜 이런 일이 발생합니까? 어떻게해야합니까?


3
어쩌면 html-string을 전달하는 대신 Document.createElement ()를 사용하면 시간이 훨씬 쉬워 질 것입니다. 결국 더 나은 코드로 끝날 것이라고 생각합니다.
admcfajn

1
노드를 다시 반복하고 문자열에 연결할 수없는 이벤트를 연결해야합니다
Eugen Sunic

좋은 질문은 확실히 까다로운 일입니다. 렌더링 후 이벤트를 처리하는 것이 가능할 수 있다고 생각합니다 for. jsFiddle을 테스트합니다.
Pogrindis

에 대한 위임 된 이벤트 핸들러를 작성 .card-body하고 요소 ( target)가로 시작하는 앵커 및 ID인지 확인하십시오 title.
hungerstar

1
아, 그냥 답변을 게시하려고합니다. 다시 열기로 투표 클로저는 필요하지 않습니다. 문제는 클로저가 아니라 innerHTML += ...이전에 설정된 이벤트 리스너를 종료하는 것입니다. document.createElement대신 사용하십시오 .
ggorlen

답변:


8

innerHTML이전에 연결된 모든 이벤트가 손실되므로 전체 HTML을 다시 그립니다. 사용하다insertAdjacentHTML()

for(let i=1;i<10;i++){
    document.querySelector('.card-body').insertAdjacentHTML('beforeend',`<div class="row" id="img_div">
        <div class="col-12 col-sm-12 col-md-2 text-center">
          <img src="http://placehold.it/120x80" alt="prewiew" width="120" height="80">
        </div>
        <div id="text_div" class="col-12 text-sm-center col-sm-12 text-md-left col-md-6">
        <h4 class="name"><a href="#" id="title`+i+`">Name</a></h4>
          <h4>
            <small>state</small>
          </h4>
          <h4>
            <small>city</small>
          </h4>
          <h4>
            <small>zip</small>
          </h4>
        </div>
        <div class="col-12 col-sm-12 text-sm-center col-md-4 text-md-right row">
        </div>
      </div>
     `);
   document.getElementById("title"+i).addEventListener('click', function () {
    console.log(i)
  });
}
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous">
<div class="card-body">
<!-- person -->
</div>


3
와우 .. 나는 단순한 것들로 뒤죽박죽 났고 결코 한 번도 오지 않았다 insertAdjacentHTML-매일 매일 학교, 정말 좋은.
Pogrindis

1
매우 시원하고 +1이지만 document.getElementByIdHTML을 추가 한 직후 에 사용해야하는 것은 여전히 ​​비싸고 불필요합니다 . 수백 개의 요소를 추가하는 경우 많은 트래버스 작업이 필요합니다.
ggorlen

@ggorlen 나는 관심있는 perf 영역 name이 클래스를 사용하고 요소를 통과하는 동안 이벤트를 해당 클래스에 연결하는 것이 더 효율적이라고 동의합니다.
Pogrindis

1
확실하지 않다. 실제 사용량에 따라 제 제안은 조기 최적화 일 수 있습니다. 프로파일 링 / 벤치 마크를해야합니다. 그러나 document.createElement사용할 수 있다면 일반적으로 이것이 최선의 기본 방법이라고 생각합니다. 이 게시물의 또 다른 개선 사항은 두 개의 루프를 사용하는 것입니다. 하나는 모든 HTML을 빌드하고 다른 하나는 한 번의 document.querySelectorAll호출 에서 클래스를 사용하여 모든 요소를 ​​가져 와서 이벤트 리스너를 추가하는 것입니다.
ggorlen

1
확실히 벤치마킹 가치가있는, 나는 오늘 밤에 더 좋은 것을 생각할 수 없다! :)
Pogrindis

8

+=on을 사용 innerHTML하면 HTML 내부의 요소에서 이벤트 리스너가 제거됩니다. 이를 수행하는 올바른 방법은을 사용하는 것 document.createElement입니다. 다음은 최소한의 완전한 예입니다.

for (let i = 1; i < 10; i++) {
  const anchor = document.createElement("a");
  document.body.appendChild(anchor);
  anchor.id = "title" + i;
  anchor.href= "#";
  anchor.textContent = "link " + i;
  document.getElementById("title" + i)
    .addEventListener("click", e => console.log(i));
}
a {
  margin-left: 0.3em;
}

물론, document.getElementById우리는 루프 블록에서 객체를 생성했기 때문에 여기서는 필요하지 않습니다. 이로 인해 잠재적으로 값 비싼 트리 워크가 절약됩니다.

for (let i = 1; i < 10; i++) {
  const anchor = document.createElement("a");
  document.body.appendChild(anchor);    
  anchor.href= "#";
  anchor.textContent = "link " + i;
  anchor.addEventListener("click", e => console.log(i));
}
a {
  margin-left: 0.3em;
}

예제와 같이 임의의 문자열 화 된 HTML 청크가있는 경우을 사용 createElement("div")하여 innerHTML한 번 청크에 설정하고 필요에 따라 리스너를 추가 할 수 있습니다 . 그런 다음 div를 컨테이너 요소의 자식으로 추가하십시오.

for (let i = 1; i < 10; i++) {
  const rawHTML = `<div><a href="#" id="link-${i}">link ${i}</a></div>`;
  const div = document.createElement("div");
  document.body.appendChild(div);
  div.href= "#";
  div.innerHTML = rawHTML;
  div.querySelector(`#link-${i}`)
    .addEventListener("click", e => console.log(i));
}


1
이것이 내가 고려한 접근법이며, 해결책을 제시해 줄 것이지만 다른 대답도 훌륭합니다.
Pogrindis

답변 해 주셔서 감사합니다.하지만 createElement를 사용하여 레이아웃을 만들려고했지만 실패했습니다
Luiz Adorno

1
문제 없어요. HTML 덩어리가 큰 경우 <div>, 다음 과 같은 루트 요소 컨테이너를 만들 수 있기를 바랍니다 div.innerHTML += yourHugeChunkOfHTML. 따라서 이것이 유스 케이스에서 작동하지 않는 이유는 없습니다.
ggorlen

1
권장 사항에 따라이 레이아웃을 만들려고합니다. "document.getElementById"를 여러 번 사용하고 성능이 저하되는 것을 두려워합니다.
Luiz Adorno
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.