เป็นไปได้ไหมที่จะต่อท้าย innerHTML โดยไม่ทำลายผู้ฟังเหตุการณ์ของลูกหลาน?


112

ในโค้ดตัวอย่างต่อไปนี้ฉันแนบonclickตัวจัดการเหตุการณ์เข้ากับช่วงที่มีข้อความ "foo" alert()จัดการเป็นฟังก์ชั่นที่ไม่ระบุชื่อที่ปรากฏขึ้น

อย่างไรก็ตามหากฉันกำหนดให้กับโหนดหลักตัวจัดการเหตุการณ์innerHTMLนี้onclickจะถูกทำลาย - การคลิก "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

สามารถใช้การมอบหมายงานเพื่อแก้ไขปัญหานี้ได้
dasfdsa

คำตอบ:


127

น่าเสียดายที่การมอบหมายงาน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 โดยพลการได้หรือไม่?
ไมค์

65
newcontent = document.createElement ('div'); newcontent.innerHTML = arbitrary_blob; ในขณะที่ (newcontent.firstChild) mydiv.appendChild (newcontent.firstChild);
Bobince

ดีบ๊อบ! ถ้าคุณโพสต์เป็นคำตอบที่มีรูปแบบดีฉันจะเลือกให้
ไมค์

@bobince - ฉันได้อัปเดตคำตอบพร้อมเทคนิคของคุณแล้วเนื่องจากคุณยังไม่ได้โพสต์เอง หากคุณสร้างคำตอบของคุณเองให้ย้อนกลับไป :-)
Ben Blank

2
โอ้สิ่งสุดท้ายคุณจะต้องมี "var myspan", "var newcontent" เป็นต้นเพื่อหลีกเลี่ยงการทำขยะหกใส่โดยไม่ได้ตั้งใจ
Bobince

85

ใช้.insertAdjacentHTML()ฟังรักษาเหตุการณ์และได้รับการสนับสนุนจากเบราว์เซอร์ที่สำคัญทั้งหมด เป็นการเปลี่ยนบรรทัดเดียวง่ายๆสำหรับ.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

ทางออกที่ดีที่สุด ขอบคุณ!
Dawoodjee

3

ตอนนี้มันเป็นปี 2012 และ jQuery มีฟังก์ชันต่อท้ายและต่อท้ายที่ทำเช่นนี้เพิ่มเนื้อหาโดยไม่ส่งผลกระทบต่อเนื้อหาปัจจุบัน มีประโยชน์มาก.


7
.insertAdjacentHTMLมีมาตั้งแต่ IE4
Esailija

1
@Tynach ใช่มันเป็นไซต์ JavaScript ของพวกเขาที่ทำเอกสารได้ดีที่สุด: developer.mozilla.org/en-US/docs/Web/API/Element/…
Jordon Bedwell

2
@JordonBedwell ฉันไม่รู้จริงๆว่าทำไมฉันถึงพูดในสิ่งที่ฉันทำมาก่อน คุณถูกต้อง 100% ฉันรู้สึกเหมือนตอนที่ฉันมองมันสั้น ๆ และไม่พบมัน แต่ ... จริงๆแล้วฉันไม่มีข้อแก้ตัว Esailija เชื่อมโยงไปยังหน้านั้นด้วย
Tynach

OP ไม่ได้พูดถึง jQuery
André Marcondes Teixeira

2

ด้านข้างเล็กน้อย (แต่เกี่ยวข้อง) หากคุณใช้ไลบรารี javascript เช่น jquery (v1.3) เพื่อทำการจัดการ Dom ของคุณคุณสามารถใช้ประโยชน์จากเหตุการณ์สดได้โดยคุณตั้งค่าตัวจัดการเช่น:

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

และจะนำไปใช้กับตัวเลือกนั้นตลอดเวลาระหว่างการจัดการ jquery ประเภทใดก็ได้ สำหรับการถ่ายทอดสดโปรดดู: docs.jquery.com/events/liveสำหรับการจัดการ jquery โปรดดู: docs.jquery.com/manipulation


1
OP ไม่ได้พูดถึง jQuery
André Marcondes Teixeira

2

ฉันสร้างมาร์กอัปเพื่อแทรกเป็นสตริงเนื่องจากโค้ดน้อยกว่าและอ่านง่ายกว่าการทำงานกับสิ่งที่แฟนซี

จากนั้นฉันก็ทำให้มันเป็น 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

1
มันเป็นวิธีแก้ปัญหาอย่างไร? มันทำลายทุกเหตุการณ์ !!
เดนมาร์ก

2

ใช่เป็นไปได้ถ้าคุณผูกเหตุการณ์โดยใช้แอตทริบิวต์แท็กonclick="sayHi()"โดยตรงในเทมเพลตที่คล้ายกับของคุณ<body onload="start()">วิธีนี้คล้ายกับเฟรมเวิร์กเชิงมุม / vue / react / etc นอกจากนี้คุณยังสามารถใช้<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>


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>


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

แต่ฉันไม่ต้องการแทนที่องค์ประกอบ ฉันแค่ต้องการต่อท้ายรายการใหม่ให้กับผู้ปกครอง
ไมค์

หากคุณกำลังเปลี่ยนองค์ประกอบฉันเห็นด้วย @Diodeus ไม่ใช่ .innerHTML ที่มีตัวจัดการเหตุการณ์ แต่เป็นพาเรนต์ของ .innerHtml
ใช่ - นั่นเจค

2
เจ้าของ innerHTML จะไม่สูญเสียตัวจัดการเมื่อ innerHTML มีการเปลี่ยนแปลงมีเพียงอะไรก็ตามในเนื้อหา และ JavaScript ไม่ทราบว่าคุณกำลังต่อท้ายเด็กใหม่เท่านั้นเนื่องจาก“ x.innerHTML + = y” เป็นเพียงน้ำตาลสังเคราะห์สำหรับการทำงานของสตริง“ x.innerHTML = x.innerHTML + y”
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 เพราะดูเหมือนว่าเป็นการพิมพ์พิเศษ สำหรับฉันรหัสยิ่งน้อยยิ่งดี นี่คือวิธีที่ฉันจะเพิ่ม "bar" โดยไม่ต้องแทนที่ "foo":

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

15
คำถามนี้กำลังถามว่าจะทำอย่างไรโดยไม่สูญเสียตัวจัดการเหตุการณ์ดังนั้นคำตอบของคุณจึงไม่เกี่ยวข้องและ btw นี่คือสิ่งที่ + = ทำซึ่งสั้นกว่า
wlf

2
เป็นเรื่องตลกที่ในคำตอบที่ผิดอย่างสิ้นเชิงนี้ซึ่งอ้างถึงการหลีกเลี่ยงการพิมพ์เพิ่มเติมเป็นเหตุผลประมาณครึ่งหนึ่งของโค้ดนั้นซ้ำซ้อน
JLRishe
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.