子孫のイベントリスナーを破壊せずに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 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"));
}

編集:コメントからのボブの解決策。答えを投稿してください、ボブ!それのための信用を得ます。:-)

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を追加できる代替はありますか?
マイク

64
newcontent = document.createElement( 'div'); newcontent.innerHTML = arbitrary_blob; while(newcontent.firstChild)mydiv.appendChild(newcontent.firstChild);
ボビンス2009

いいね、ボブ!それを整形式の回答として投稿した場合は、私が選択します。
マイク

@bobince —自分でまだ投稿していないので、私の答えをあなたのテクニックで更新しました。独自の答えを作成する場合は、先に進んでロールバックしてください。:-)
ベンブランク

2
最後に、誤ってグローバルをこぼさないように、「var myspan」、「var newcontent」などが必要になります。
ボビンス2009

85

を使用.insertAdjacentHTML()すると、イベントリスナーが保持され、すべての主要なブラウザでサポートされます。これは、の単純な1行の置き換えです.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

最良のソリューション。ありがとうございました!
Dawoodjee

3

現在、それは2012年です。jQueryには、これを正確に実行する追加機能と追加機能があり、現在のコンテンツに影響を与えずにコンテンツを追加します。非常に便利。



1
:@Tynachはい、それは、それが最善の文書という彼らのJavaScriptのサイトだんdeveloper.mozilla.org/en-US/docs/Web/API/Element/...
ジョーダンBedwell

2
@JordonBedwell、正直に言って、なぜ私が以前何をしたかを言ったのか分かりません。あなたは100%正しいです。ちょっと調べてみたら気付かなかったような気がしますが・・・正直申し訳ありません。エサイリアはそのページにリンクしている。
Tynach

OPはjQueryについて話していません
–AndréMarcondes Teixeira '15

2

少し(ただし関連していますが)、jquery(v1.3)などのJavaScriptライブラリを使用してdom操作を行う場合は、次のようなハンドラーを設定するライブイベントを利用できます。

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

そして、それはあらゆる種類のjquery操作の間、常にそのセレクターに適用されます。ライブイベントについては、docs.jquery.com / events / liveを参照してください。jquery操作については、docs.jquery.com / manipulationを参照してください。


1
OPはjQueryについて話していません
–AndréMarcondes Teixeira '15

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

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

はい、あなたのonclick="sayHi()"ようなテンプレートでタグ属性を使用してイベントを直接バインドすることは可能です<body onload="start()">-このアプローチはフレームワークangular / 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>


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、2009年

しかし、要素を置き換えたくありません。親に新しいものを追加したいだけです。
マイク

要素を交換する場合、@ Diodeusに同意します。イベントハンドラーを持つのは.innerHTMLではなく、.innerHtmlの親です。
はい-そのジェイク。

2
innerHTMLの所有者は、innerHTMLが変更されてもハンドラーを失うことはなく、コンテンツのすべてを失います。また、「x.innerHTML + = y」は文字列操作「x.innerHTML = x.innerHTML + y」の構文上の砂糖にすぎないため、JavaScriptは新しい子を追加するだけであることを認識していません。
ボビンス2009

再接続する必要はありませんmydiv.onclick。内部スパンのonclickのみがによってオーバーライドされinnerHTML +=ます。
クレセントフレッシュ

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
この質問は、イベントハンドラーを失うことなくこれを行う方法を尋ねているので、あなたの答えは関係ありません。そして、これは+ =が行うことです。
かなり

2
正当な理由として余分な入力を回避することを引用するこの完全に間違った答えでは、コードの約半分が冗長であることは陽気です。
JLRishe 2017年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.