자손의 이벤트 리스너를 파괴하지 않고 innerHTML에 추가 할 수 있습니까?


112

다음 예제 코드에서는 onclick"foo"라는 텍스트가 포함 된 범위에 이벤트 핸들러를 연결합니다 . 핸들러는 alert().

그러나 부모 노드의에 할당하면 innerHTMLonclick이벤트 핸들러가 파괴됩니다. "foo"를 클릭하면 경고 상자가 표시되지 않습니다.

이 문제를 해결할 수 있습니까?

<html>
 <head>
 <script type="text/javascript">

  function start () {
    myspan = document.getElementById("myspan");
    myspan.onclick = function() { alert ("hi"); };

    mydiv = document.getElementById("mydiv");
    mydiv.innerHTML += "bar";
  }

 </script>
 </head>

 <body onload="start()">
   <div id="mydiv" style="border: solid red 2px">
     <span id="myspan">foo</span>
   </div>
 </body>

</html>

이 질문은 '양식에 새 필드를 추가하면 사용자 입력이 지워짐'문제와 관련이 있습니다. 선택한 답변은이 두 가지 문제를 모두 매우 훌륭하게 해결합니다.
MattT 2014

이벤트 위임을 사용하여이 문제를 해결할 수 있습니다.
dasfdsa

답변:


126

안타깝게도에 할당 innerHTML하면 추가하려는 경우에도 모든 자식 요소가 파괴됩니다. 자식 노드 (및 해당 이벤트 처리기)를 보존하려면 DOM 함수 를 사용해야 합니다 .

function start() {
    var myspan = document.getElementById("myspan");
    myspan.onclick = function() { alert ("hi"); };

    var mydiv = document.getElementById("mydiv");
    mydiv.appendChild(document.createTextNode("bar"));
}

편집 : 의견에서 Bob의 솔루션. 답을 게시하세요, 밥! 그것에 대한 신용을 얻으십시오. :-)

function start() {
    var myspan = document.getElementById("myspan");
    myspan.onclick = function() { alert ("hi"); };

    var mydiv = document.getElementById("mydiv");
    var newcontent = document.createElement('div');
    newcontent.innerHTML = "bar";

    while (newcontent.firstChild) {
        mydiv.appendChild(newcontent.firstChild);
    }
}

HTML의 임의의 blob을 추가 할 수있는 대체물이 있습니까?
mike

64
newcontent = document.createElement ( 'div'); newcontent.innerHTML = 임의 _blob; while (newcontent.firstChild) mydiv.appendChild (newcontent.firstChild);
bobince

좋아, 밥! 올바른 형식의 답변으로 게시하면 선택하겠습니다.
mike

@bobince — 귀하가 아직 직접 게시하지 않았기 때문에 귀하의 기술로 제 답변을 업데이트했습니다. 자신만의 답변을 작성하는 경우 계속 진행하여 롤백하십시오. :-)
Ben Blank

2
아, 마지막으로 "var myspan", "var newcontent"등이 실수로 전역을 유출하지 않도록해야합니다.
bobince

85

사용하면 .insertAdjacentHTML()이벤트 리스너가 유지 되며 모든 주요 브라우저에서 지원됩니다 . .NET의 간단한 한 줄 대체입니다 .innerHTML.

var html_to_insert = "<p>New paragraph</p>";

// with .innerHTML, destroys event listeners
document.getElementById('mydiv').innerHTML += html_to_insert;

// with .insertAdjacentHTML, preserves event listeners
document.getElementById('mydiv').insertAdjacentHTML('beforeend', html_to_insert);

'beforeend'요소에 인수 지정은 HTML 콘텐츠를 삽입합니다. 옵션은 'beforebegin', 'afterbegin', 'beforeend',와 'afterend'. 해당 위치는 다음과 같습니다.

<!-- beforebegin -->
<div id="mydiv">
  <!-- afterbegin -->
  <p>Existing content in #mydiv</p>
  <!-- beforeend -->
</div>
<!-- afterend -->

당신은 내 하루를 구했습니다!
Tural Asgar 2019

최고의 솔루션. 감사합니다!
Dawoodjee

3

이제 2012 년이되었습니다. jQuery는이를 정확히 수행하는 추가 및 앞에 추가 기능을 가지고 있으며 현재 콘텐츠에 영향을주지 않고 콘텐츠를 추가합니다. 굉장히 유용하다.


7
.insertAdjacentHTMLIE4 이후 주변왔다
Esailija

1
@Tynach 그렇습니다. 최고의 문서화는 JavaScript 사이트입니다. developer.mozilla.org/en-US/docs/Web/API/Element/…
Jordon Bedwell

2
@JordonBedwell, 솔직히 왜 내가 전에 한 일을 말했는지 모르겠습니다. 당신이 100 % 맞습니다. 잠깐 들여다 보니 찾지 못한 것 같았는데 .. 솔직히 변명 할 게 없어요. Esailija는 심지어 그 페이지로 연결 됩니다.
Tynach

영업 이익은 jQuery를 얘기하지 않습니다
앙드레 Marcondes 텍사스

2

약간의 (하지만 관련이있는) 제쳐두고, jquery (v1.3)와 같은 자바 스크립트 라이브러리를 사용하여 dom 조작을 수행하는 경우 라이브 이벤트를 사용하여 다음과 같은 핸들러를 설정할 수 있습니다.

 $("#myspan").live("click", function(){
  alert('hi');
});

모든 종류의 jquery 조작 중에 항상 해당 선택기에 적용됩니다. 라이브 이벤트를 참조하십시오 : docs.jquery.com/events/live JQuery와 조작은 다음을 참조하십시오 docs.jquery.com/manipulation를


1
영업 이익은 jQuery를 얘기하지 않습니다
앙드레 Marcondes 텍사스

2

코드가 적고 멋진 dom 항목으로 작업하는 것보다 읽기가 쉽기 때문에 문자열로 삽입 할 마크 업을 만들었습니다.

그런 다음 해당 요소의 유일한 자식을 가져와 본문에 첨부 할 수 있도록 임시 요소의 innerHTML을 만들었습니다.

var html = '<div>';
html += 'Hello div!';
html += '</div>';

var tempElement = document.createElement('div');
tempElement.innerHTML = html;
document.getElementsByTagName('body')[0].appendChild(tempElement.firstChild);

2

something.innerHTML + = '원하는대로 추가';

그것은 나를 위해 일했습니다. 이 솔루션을 사용하여 입력 텍스트에 버튼을 추가했습니다.


이것은 가장 간단한 방법이었습니다. 감사합니다!
demo7up

4
모든 이벤트를 파괴합니다.
Tural Asgar 19.11.

1
실제로 어떻게 해결책입니까? 모든 이벤트를 파괴합니다 !!
덴마크어

1

또 다른 대안이 있습니다 setAttribute. 이벤트 리스너를 추가하는 대신 사용 하는 것입니다. 이렇게 :

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>Demo innerHTML and event listeners</title>
<style>
    div {
        border: 1px solid black;
        padding: 10px;
    }
</style>
</head>
<body>
    <div>
        <span>Click here.</span>
    </div>
    <script>
        document.querySelector('span').setAttribute("onclick","alert('Hi.')");
        document.querySelector('div').innerHTML += ' Added text.';
    </script>
</body>
</html>


1

예, angular / vue / react / etc 프레임 워크와 유사한이 접근 방식과 유사한 onclick="sayHi()"템플릿에서 태그 속성을 사용하여 이벤트를 직접 바인딩하면 가능합니다 <body onload="start()">. 여기<template> 와 같이 '동적'html에서 작업 하는 데 사용할 수도 있습니다 . 엄격한 눈에 거슬리지 않는 js는 아니지만 소규모 프로젝트 에는 허용 됩니다.

function start() {
  mydiv.innerHTML += "bar";
}

function sayHi() {
  alert("hi");
}
<body onload="start()">
  <div id="mydiv" style="border: solid red 2px">
    <span id="myspan" onclick="sayHi()">foo</span>
  </div>
</body>


0

이벤트 핸들러를 잃는 것은 IMO, Javascript가 DOM을 처리하는 방식의 버그입니다. 이 동작을 방지하려면 다음을 추가 할 수 있습니다.

function start () {
  myspan = document.getElementById("myspan");
  myspan.onclick = function() { alert ("hi"); };

  mydiv = document.getElementById("mydiv");
  clickHandler = mydiv.onclick;  // add
  mydiv.innerHTML += "bar";
  mydiv.onclick = clickHandler;  // add
}

2
나는 이것을 버그라고 생각하지 않는다. 요소를 교체한다는 것은 완전히 교체한다는 의미입니다. 이전에 있었던 것을 상속해서는 안됩니다.
Diodeus-James MacFarlane

하지만 요소를 바꾸고 싶지 않습니다. 부모에게 새로운 것을 추가하고 싶습니다.
mike

요소를 교체하는 경우 @Diodeus에 동의합니다. 이벤트 핸들러가있는 .innerHTML이 아니라 .innerHtml의 부모입니다.
그래-그 Jake.

2
innerHTML의 소유자는 innerHTML이 변경 될 때 처리기를 잃지 않고 내용에있는 모든 항목 만 손실합니다. "x.innerHTML + = y"는 문자열 연산 "x.innerHTML = x.innerHTML + y"에 대한 구문 적 설탕 일 뿐이므로 JavaScript는 새 자식 만 추가한다는 것을 알지 못합니다.
bobince

을 다시 첨부 할 필요가 없습니다 mydiv.onclick. 내부 스팬의 onclick 만 innerHTML +=.
Crescent Fresh

0

다음과 같이 할 수 있습니다.

var anchors = document.getElementsByTagName('a'); 
var index_a = 0;
var uls = document.getElementsByTagName('UL'); 
window.onload=function()          {alert(anchors.length);};
for(var i=0 ; i<uls.length;  i++)
{
    lis = uls[i].getElementsByTagName('LI');
    for(var j=0 ;j<lis.length;j++)
    {
        var first = lis[j].innerHTML; 
        string = "<img src=\"http://g.etfv.co/" +  anchors[index_a++] + 
            "\"  width=\"32\" 
        height=\"32\" />   " + first;
        lis[j].innerHTML = string;
    }
}

0

가장 쉬운 방법은 배열을 사용하고 여기에 요소를 푸시 한 다음 배열 후속 값을 배열에 동적으로 삽입하는 것입니다. 내 코드는 다음과 같습니다.

var namesArray = [];

function myclick(){
    var readhere = prompt ("Insert value");
    namesArray.push(readhere);
    document.getElementById('demo').innerHTML= namesArray;
}

0

헤더와 데이터가있는 모든 객체 배열의 경우.jsfiddle

https://jsfiddle.net/AmrendraKumar/9ac75Lg0/2/

<table id="myTable" border='1|1'></table>

<script>
  const userObjectArray = [{
    name: "Ajay",
    age: 27,
    height: 5.10,
    address: "Bangalore"
  }, {
    name: "Vijay",
    age: 24,
    height: 5.10,
    address: "Bangalore"
  }, {
    name: "Dinesh",
    age: 27,
    height: 5.10,
    address: "Bangalore"
  }];
  const headers = Object.keys(userObjectArray[0]);
  var tr1 = document.createElement('tr');
  var htmlHeaderStr = '';
  for (let i = 0; i < headers.length; i++) {
    htmlHeaderStr += "<th>" + headers[i] + "</th>"
  }
  tr1.innerHTML = htmlHeaderStr;
  document.getElementById('myTable').appendChild(tr1);

  for (var j = 0; j < userObjectArray.length; j++) {
    var tr = document.createElement('tr');
    var htmlDataString = '';
    for (var k = 0; k < headers.length; k++) {
      htmlDataString += "<td>" + userObjectArray[j][headers[k]] + "</td>"
    }
    tr.innerHTML = htmlDataString;
    document.getElementById('myTable').appendChild(tr);
  }

</script>

-5

나는 게으른 프로그래머입니다. 추가 타이핑처럼 보이기 때문에 DOM을 사용하지 않습니다. 나에게는 코드가 적을수록 좋습니다. "foo"를 대체하지 않고 "bar"를 추가하는 방법은 다음과 같습니다.

function start(){
var innermyspan = document.getElementById("myspan").innerHTML;
document.getElementById("myspan").innerHTML=innermyspan+"bar";
}

15
이 질문은 이벤트 핸들러를 잃지 않고이를 수행하는 방법을 묻는 것이므로 귀하의 답변은 관련이 없습니다. btw 이것은 + =
가하는

2
정당화로 추가 입력을 피하는 것을 인용하는 완전히 잘못된 대답에서 코드의 약 절반이 중복된다는 것은 재밌습니다.
JLRishe
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.