Чи можна додавати до 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

Відповіді:


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?
Майк

64
newcontent = document.createElement ('div'); newcontent.innerHTML = довільний_blob; while (newcontent.firstChild) mydiv.appendChild (newcontent.firstChild);
bobince

Приємно, Бобе! Якщо ви опублікуєте це як добре відформатовану відповідь, я виберу її.
Майк

@bobince - я оновив свою відповідь вашою технікою, оскільки ви її ще не опублікували. Якщо ви створюєте свою власну відповідь, ідіть і відкочуйте мою назад. :-)
Бен Бланк

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

Ти врятував мій день!
Турал Асгар

Найкраще рішення. Дякую!
Dawoodjee

3

Тепер це 2012 рік, і jQuery має функції додавання та додавання, які роблять саме це, додають вміст, не впливаючи на поточний вміст. Дуже корисний.


7
.insertAdjacentHTMLіснує вже з IE4
Esailija

1
@Tynach так, це так, саме їхній JavaScript-сайт найкраще документує це: developer.mozilla.org/en-US/docs/Web/API/Element/…
Джордон Бедвелл

2
@JordonBedwell, я, чесно кажучи, поняття не маю, чому я сказав те, що робив раніше. Ви на 100% праві. Мені здається, що я коротко заглянув у нього і не зміг його знайти, але ... Чесно кажучи, у мене немає виправдання. Ісаїлія навіть посилається на цю сторінку.
Тинах

ОП не говорить про jQuery
Андре Маркондес Тейшейра

2

Як невеликий (але пов'язаний з цим) засіб, якщо ви використовуєте бібліотеку javascript, наприклад jquery (v1.3), для маніпуляції з домом, ви можете використовувати події в реальному часі, за допомогою яких налаштовуєте обробник, наприклад:

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

і він буде застосований до цього селектора під час будь-якого маніпулювання jquery. Про події наживо дивіться: docs.jquery.com/events/live для маніпулювання jquery див.: Docs.jquery.com/manipulation


1
ОП не говорить про jQuery
Андре Маркондес Тейшейра

2

Я створив свою розмітку для вставки як рядок, оскільки це менше коду та легше для читання, ніж робота з химерними домашніми речами.

Тоді я зробив це внутрішньоHTML тимчасовим елементом просто для того, щоб я міг взяти єдину дитину цього елемента і прикріпити до тіла.

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
Це руйнує всі події.
Турал Асгар

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 / тощо. Ви також можете використовувати <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. Обробник події не є .innerHTML, а батьківський .innerHtml.
Так - той Джейк.

2
Власник innerHTML не втрачає обробників, коли його внутрішнійHTML змінюється, лише все, що міститься в його вмісті. І JavaScript не знає, що ви додаєте лише нових дітей, оскільки "x.innerHTML + = y" - це лише синтаксичний цукор для рядкової операції "x.innerHTML = x.innerHTML + y".
bobince

Вам не потрібно повторно вкладати файли 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, оскільки це здається додатковим введенням тексту. Для мене, чим менше код, тим краще. Ось як я додав би "bar", не замінюючи "foo":

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

15
Це питання задає питання, як це зробити, не втрачаючи обробників подій, тому ваша відповідь не актуальна, і btw це те, що + = робить, що набагато коротше
wlf

2
Весело, що в цій абсолютно неправильній відповіді, яка цитує уникнення зайвого введення тексту як виправдання, приблизно половина коду є зайвою.
JLRishe
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.