Apakah mungkin untuk menambahkan innerHTML tanpa merusak event listener turunan?

112

Dalam kode contoh berikut, saya melampirkan onclickevent handler ke span yang berisi teks "foo". Penangan adalah fungsi anonim yang memunculkan filealert() .

Namun, jika saya menetapkan ke node induk innerHTML, inionclick penanganan peristiwa akan dihancurkan - mengklik "foo" gagal memunculkan kotak peringatan.

Apakah ini bisa diperbaiki?

<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>
mike
sumber
Pertanyaan ini terkait dengan masalah 'menambahkan bidang baru ke formulir menghapus masukan pengguna'. Jawaban yang dipilih memperbaiki kedua masalah ini dengan sangat baik.
MattT
Delegasi acara dapat digunakan untuk mengatasi masalah ini.
dasfdsa

Jawaban:

126

Sayangnya, penugasan innerHTMLmenyebabkan kehancuran semua elemen turunan, meskipun Anda mencoba menambahkan. Jika Anda ingin mempertahankan node turunan (dan penangan kejadiannya), Anda harus menggunakan fungsi DOM :

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

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

Edit: solusi Bob, dari komentar. Posting jawaban Anda, Bob! Dapatkan pujian untuk itu. :-)

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);
    }
}
Ben Blank
sumber
Apakah ada pengganti yang dapat menambahkan gumpalan HTML yang berubah-ubah?
mike
64
newcontent = document.createElement ('div'); newcontent.innerHTML = arbitrary_blob; while (newcontent.firstChild) mydiv.appendChild (newcontent.firstChild);
bobince
Bagus, Bob! Jika Anda mempostingnya sebagai jawaban yang diformat dengan baik, saya akan memilihnya.
mike
@bobince - Saya telah memperbarui jawaban saya dengan teknik Anda, karena Anda sendiri belum mempostingnya. Jika Anda membuat jawaban Anda sendiri, lanjutkan dan putar kembali jawaban saya. :-)
Ben Blank
2
Oh, satu hal lagi, Anda ingin “var myspan”, “var newcontent”, dll. Untuk menghindari tumpahan global secara tidak sengaja.
bobince
85

Menggunakan .insertAdjacentHTML()mempertahankan event listener, dan didukung oleh semua browser utama . Ini adalah pengganti satu baris sederhana untuk .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);

The 'beforeend'menspesifikasikan argumen di mana dalam elemen untuk memasukkan konten HTML. Pilihan yang 'beforebegin', 'afterbegin', 'beforeend', dan 'afterend'. Lokasi yang sesuai adalah:

<!-- beforebegin -->
<div id="mydiv">
  <!-- afterbegin -->
  <p>Existing content in #mydiv</p>
  <!-- beforeend -->
</div>
<!-- afterend -->
Josh de Leeuw
sumber
Anda menyelamatkan hari saya!
Tural Asgar
Solusi terbaik. Terima kasih!
Dawoodjee
3

Sekarang, tahun 2012, dan jQuery telah menambahkan dan menambahkan fungsi yang melakukan hal ini dengan tepat, menambahkan konten tanpa memengaruhi konten saat ini. Sangat berguna.

rocketsarefast
sumber
7
.insertAdjacentHTMLtelah ada sejak IE4
Esailija
1
@Tynach ya, itu situs JavaScript mereka yang mendokumentasikannya dengan baik: developer.mozilla.org/en-US/docs/Web/API/Element/…
Jordon Bedwell
2
@JordonBedwell, sejujurnya saya tidak tahu mengapa saya mengatakan apa yang saya lakukan sebelumnya. Anda 100% benar. Aku merasa seperti saat aku melihatnya sebentar dan tidak bisa menemukannya, tapi ... Sejujurnya aku tidak punya alasan. Esailija bahkan menautkan ke halaman itu.
Tynach
OP tidak berbicara tentang jQuery
André Marcondes Teixeira
2

Sebagai bantuan kecil (tetapi terkait), jika Anda menggunakan pustaka javascript seperti jquery (v1.3) untuk melakukan manipulasi dom Anda, Anda dapat menggunakan acara langsung di mana Anda menyiapkan penangan seperti:

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

dan itu akan diterapkan ke selektor itu setiap saat selama segala jenis manipulasi jquery. Untuk acara langsung lihat: docs.jquery.com/events/live untuk manipulasi jquery lihat: docs.jquery.com/manipulation

Cargowire
sumber
1
OP tidak berbicara tentang jQuery
André Marcondes Teixeira
2

Saya membuat markup saya untuk disisipkan sebagai string karena lebih sedikit kode dan lebih mudah dibaca daripada bekerja dengan barang-barang dom mewah.

Kemudian saya membuatnya menjadi innerHTML dari elemen sementara supaya saya bisa mengambil satu-satunya anak dari elemen itu dan menempelkannya ke tubuh.

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

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

something.innerHTML + = 'tambahkan apapun yang Anda inginkan';

itu berhasil untuk saya. Saya menambahkan tombol ke teks input menggunakan solusi ini

KawaiKx
sumber
Ini adalah metode paling sederhana yang saya temui, terima kasih!
demo7up
4
Itu menghancurkan semua peristiwa.
Tural Asgar
1
bagaimana ini benar-benar sebuah solusi? itu menghancurkan semua acara !!
Denmark
1

Ada alternatif lain: menggunakan setAttributedaripada menambahkan pendengar acara. Seperti ini:

<!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>

Frank Conijn
sumber
1

Ya, itu mungkin jika Anda mengikat peristiwa menggunakan atribut tag onclick="sayHi()"secara langsung di template yang mirip dengan Anda <body onload="start()">- pendekatan ini mirip dengan frameworks angular / vue / react / etc. Anda juga dapat menggunakan <template>untuk mengoperasikan html 'dinamis' seperti di sini . Ini bukan js yang tidak mengganggu namun dapat diterima untuk proyek kecil

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>

Kamil Kiełczewski
sumber
0

Kehilangan penangan peristiwa, IMO, adalah bug dalam cara Javascript menangani DOM. Untuk menghindari perilaku ini, Anda dapat menambahkan yang berikut ini:

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
}
Ya - Jake itu.
sumber
2
Saya tidak menganggap ini bug. Mengganti elemen berarti Anda menggantinya sepenuhnya. Seharusnya tidak mewarisi apa yang ada sebelumnya.
Diodeus - James MacFarlane
Tapi saya tidak ingin mengganti elemen; Saya hanya ingin menambahkan yang baru ke induknya.
mike
Jika Anda mengganti elemen, saya setuju, @Diodeus. Bukan .innerHTML yang memiliki pengendali kejadian, tetapi induk .innerHtml.
Ya - Jake itu.
2
Pemilik innerHTML tidak kehilangan penangan ketika innerHTMLnya diubah, hanya apa saja yang ada di isinya. Dan JavaScript tidak tahu bahwa Anda hanya menambahkan anak baru, karena "x.innerHTML + = y" hanya gula sintaksis untuk operasi string "x.innerHTML = x.innerHTML + y".
bobince
Anda tidak perlu memasang kembali mydiv.onclick. Hanya klik rentang dalam yang diganti oleh innerHTML +=.
Crescent Fresh
0

Anda bisa melakukannya seperti ini:

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;
    }
}
Rishabh gupta
sumber
0

Cara termudah adalah dengan menggunakan array dan mendorong elemen ke dalamnya dan kemudian memasukkan nilai-nilai berikutnya ke dalam array secara dinamis. Ini kode saya:

var namesArray = [];

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

Untuk semua larik objek dengan header dan data.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>
AKS
sumber
-5

Saya seorang programmer yang malas. Saya tidak menggunakan DOM karena sepertinya pengetikan tambahan. Bagi saya, semakin sedikit kode semakin baik. Berikut cara saya menambahkan "bar" tanpa mengganti "foo":

function start(){
var innermyspan = document.getElementById("myspan").innerHTML;
document.getElementById("myspan").innerHTML=innermyspan+"bar";
}
lucious
sumber
15
Pertanyaan ini menanyakan bagaimana melakukan ini tanpa kehilangan event handler, jadi jawaban Anda tidak relevan, dan btw inilah yang + = lakukan, yang jauh lebih pendek
wlf
2
Sangat lucu bahwa dalam jawaban yang sepenuhnya salah ini yang mengutip menghindari pengetikan tambahan sebagai pembenaran, sekitar setengah dari kode tersebut berlebihan.
JLRishe