Ereignis an dynamische Elemente in Javascript anhängen

90

Ich versuche, HTML-Daten dynamisch in eine Liste einzufügen, die dynamisch erstellt wird, aber wenn ich versuche, ein Onclick-Ereignis für die dynamisch erstellte Schaltfläche anzuhängen, wird das Ereignis nicht ausgelöst. Lösung wäre sehr dankbar.

Javascript-Code:

document.addEventListener('DOMContentLoaded', function () {
document.getElementById('btnSubmit').addEventListener('click', function () {
    var name = document.getElementById('txtName').value;
    var mobile = document.getElementById('txtMobile').value;
    var html = '<ul>';
    for (i = 0; i < 5; i++) {
        html = html + '<li>' + name + i + '</li>';
    }
    html = html + '</ul>';

    html = html + '<input type="button" value="prepend" id="btnPrepend" />';
    document.getElementsByTagName('form')[0].insertAdjacentHTML('afterend', html);
});

document.getElementById('btnPrepend').addEventListener('click', function () {
    var html = '<li>Prepending data</li>';
    document.getElementsByTagName('ul')[0].insertAdjacentHTML('afterbegin', html);
});

});

HTML Quelltext:

<form>
    <div class="control">
        <label>Name</label>
        <input id="txtName" name="txtName" type="text" />
    </div>
    <div class="control">
        <label>Mobile</label>
        <input id="txtMobile" type="text" />
    </div>
    <div class="control">
        <input id="btnSubmit" type="button" value="submit" />
    </div>
</form>
Manju
quelle
Wie erstellen Sie das HTML?
jim0thy
1
Ich würde sagen, es liegt daran, dass das Element nicht vorhanden ist, wenn Sie versuchen, den Ereignis-Listener anzuhängen. - Werfen
Sie
1
Verschieben Sie Ihren addEventListener in den Ereignis-Listener von btnSubmit
slebetman

Antworten:

199

Dies liegt daran, dass Ihr Element dynamisch erstellt wird. Sie sollten die Ereignisdelegierung verwenden , um das Ereignis zu behandeln.

 document.addEventListener('click',function(e){
    if(e.target && e.target.id== 'brnPrepend'){
          //do something
     }
 });

jquery macht es einfacher:

 $(document).on('click','#btnPrepend',function(){//do something})

Hier ist ein Artikel über Event Delegation Ereignis Delegation Artikel

Jilykate
quelle
1
Vielen Dank! Manchmal funktioniert eine reine JS-Lösung auf dem iPhone nicht, aber wenn Sie beispielsweise ein Dokument auf einem anderen übergeordneten Div ändern, funktioniert dies. Was ist die beste Lösung für die Delegation?
Kholiavko
3
@Kholiavko Ich würde sagen, solange das "übergeordnete" Element nicht dynamisch erstellt wird, sollte es funktionieren. Ich würde den Ereignishandler an das erste übergeordnete Element des dynamischen Elements create d binden, damit es keine Konflikte zwischen verschiedenen Ereignishandlern gibt
jilykate
1
Aber manchmal funktioniert es nicht auf dem iPhone, selbst das "übergeordnete" Element wird nicht dynamisch erstellt. Hier ist exapmle , es funktioniert auf allen Browsern, aber nicht auf dem iPhone (sicher und sogar Chrom). Und ich verstehe nicht warum.
Kholiavko
2
@Kholiavko das ist nur, weil Codepen eine Sandbox verwenden, um alles zu verpacken. Wenn Sie diesen Code wirklich auf einer normalen Seite schreiben, wird es funktionieren
jilykate
2
Erwägen Sie das Hinzufügen eines zusätzlichen Gleichheitszeichens (===), um unerwarteten Zwang zu vermeiden
Staccato
17

Es gibt eine Problemumgehung, indem Sie Klicks erfassen document.bodyund dann das Ereignisziel überprüfen.

document.body.addEventListener( 'click', function ( event ) {
  if( event.srcElement.id == 'btnSubmit' ) {
    someFunc();
  };
} );
Manasov Daniel
quelle
2
Oder nächster Container
Mplungjan
Perfekte Antwort!
Rahul Purohit
WOW das ist unglaublich, spart so viel Zeit und Code! :)
Anna Medyukh
Hinweis: event.srcElement ist veraltet. Es wird empfohlen, stattdessen event.target zu verwenden.
dsanchez
12

Sie müssen das Ereignis nach dem Einfügen von Elementen anhängen, sodass Sie kein globales Ereignis an Ihr, documentsondern ein bestimmtes Ereignis an die eingefügten Elemente anhängen .

z.B

document.getElementById('form').addEventListener('submit', function(e) {
  e.preventDefault();
  var name = document.getElementById('txtName').value;
  var idElement = 'btnPrepend';
  var html = `
    <ul>
      <li>${name}</li>
    </ul>
    <input type="button" value="prepend" id="${idElement}" />
  `;
  /* Insert the html into your DOM */
  insertHTML('form', html);
  /* Add an event listener after insert html */
  addEvent(idElement);
});

const insertHTML = (tag = 'form', html, position = 'afterend', index = 0) => {
  document.getElementsByTagName(tag)[index].insertAdjacentHTML(position, html);
}
const addEvent = (id, event = 'click') => {
  document.getElementById(id).addEventListener(event, function() {
    insertHTML('ul', '<li>Prepending data</li>', 'afterbegin')
  });
}
<form id="form">
  <div>
    <label for="txtName">Name</label>
    <input id="txtName" name="txtName" type="text" />
  </div>
  <input type="submit" value="submit" />
</form>

R3tep
quelle
2
Dieser Ansatz funktioniert nur für CSR (Client Side Rendering). Dies funktioniert nicht für SSR (Server-Side Rendering). Damit es sowohl für CSR als auch für SSR funktioniert, ist Event Delegation die Lösung:javascript document.addEventListener('click',function(e){ if(e.target && e.target.id== 'brnPrepend'){ //do something } });
Yatendra Goel
Aus Leistungsgründen ist dieser Ansatz jedoch besser. Es hängt also von Ihrem Anwendungsfall ab.
R3tep
8

Der Unterschied besteht darin, wie Sie Elemente im DOM erstellen und anhängen.

Wenn Sie ein Element über erstellen document.createElement, fügen Sie einen Ereignis-Listener hinzu und hängen Sie ihn an das DOM an. Ihre Ereignisse werden ausgelöst.

Wenn Sie ein Element als Zeichenfolge wie folgt erstellen: html + = "<li> test </ li>" `, ist das Element technisch nur eine Zeichenfolge. Strings können keine Ereignis-Listener haben.

Eine Lösung besteht darin, jedes Element mit zu erstellen document.createElementund diese dann direkt einem DOM-Element hinzuzufügen.

// Sample
let li = document.createElement('li')
document.querySelector('ul').appendChild(li)
Richard
quelle
3

Sie können etwas Ähnliches tun:

// Get the parent to attatch the element into
var parent = document.getElementsByTagName("ul")[0];

// Create element with random id
var element = document.createElement("li");
element.id = "li-"+Math.floor(Math.random()*9999);

// Add event listener
element.addEventListener("click", EVENT_FN);

// Add to parent
parent.appendChild(element);
PRDeving
quelle
2
var __ = function(){
    this.context  = [];
    var self = this;
    this.selector = function( _elem, _sel ){
        return _elem.querySelectorAll( _sel );
    }
          this.on = function( _event, _element, _function ){
              this.context = self.selector( document, _element );
              document.addEventListener( _event, function(e){
                  var elem = e.target;
                  while ( elem != null ) {
                      if( "#"+elem.id == _element || self.isClass( elem, _element ) || self.elemEqal( elem ) ){
                          _function( e, elem );
                      }
                      elem = elem.parentElement;
                  }
              }, false );
     };

     this.isClass = function( _elem, _class ){
        var names = _elem.className.trim().split(" ");
        for( this.it = 0; this.it < names.length; this.it++ ){
            names[this.it] = "."+names[this.it];
        }
        return names.indexOf( _class ) != -1 ? true : false;
    };

    this.elemEqal = function( _elem ){
        var flg = false;
        for( this.it = 0; this.it < this.context.length;  this.it++ ){
            if( this.context[this.it] === _elem && !flg ){
                flg = true;
            }
        }
        return flg;
    };

}

    function _( _sel_string ){
        var new_selc = new __( _sel_string );
        return new_selc;
    }

Jetzt können Sie Ereignisse wie registrieren,

_( document ).on( "click", "#brnPrepend", function( _event, _element ){
      console.log( _event );
      console.log( _element );
      // Todo

  });

Browser-Unterstützung

Chrome - 4.0, Edge - 9.0, Firefox - 3.5 Safari - 3.2, Opera - 10.0 und höher

paranjothi
quelle
2

Ich habe eine kleine Bibliothek erstellt, um dies zu unterstützen: Bibliotheksquelle auf GitHub

<script src="dynamicListener.min.js"></script>
<script>
// Any `li` or element with class `.myClass` will trigger the callback, 
// even elements created dynamically after the event listener was created.
addDynamicEventListener(document.body, 'click', '.myClass, li', function (e) {
    console.log('Clicked', e.target.innerText);
});
</script>

Die Funktionalität ähnelt jQuery.on ().

Die Bibliothek verwendet die Element.matches () -Methode, um das Zielelement anhand des angegebenen Selektors zu testen. Wenn ein Ereignis ausgelöst wird, wird der Rückruf nur aufgerufen, wenn das Zielelement mit dem angegebenen Selektor übereinstimmt.

XCS
quelle
0

Ich weiß, dass das Thema zu alt ist, aber ich habe mir einige Minuten Zeit genommen, um einen sehr nützlichen Code zu erstellen, der mit pure gut und sehr einfach funktioniert JAVASCRIPT. Hier ist der Code mit einem einfachen Beispiel:

String.prototype.addEventListener=function(eventHandler, functionToDo){
  let selector=this;
  document.body.addEventListener(eventHandler, function(e){
    e=(e||window.event);
    e.preventDefault();
    const path=e.path;
    path.forEach(function(elem){
      const selectorsArray=document.querySelectorAll(selector);
      selectorsArray.forEach(function(slt){
        if(slt==elem){
          if(typeof functionToDo=="function") functionToDo(el=slt, e=e);
        }
      });
    });
  });
}

// And here is how we can use it actually !

"input[type='number']".addEventListener("click", function(element, e){
	console.log( e ); // Console log the value of the current number input
});
<input type="number" value="25">
<br>
<input type="number" value="15">
<br><br>
<button onclick="addDynamicInput()">Add a Dynamic Input</button>
<script type="text/javascript">
  function addDynamicInput(){
    const inpt=document.createElement("input");
          inpt.type="number";
          inpt.value=Math.floor(Math.random()*30+1);
    document.body.prepend(inpt);
  }
</script>

Adnane Ar
quelle
0

Ich habe eine einfache Funktion dafür gemacht.

Mit dieser _caseFunktion können Sie nicht nur das Ziel abrufen, sondern auch das übergeordnete Element, an das Sie das Ereignis binden.

Die Rückruffunktion gibt das Ereignis zurück, das das Ziel ( evt.target) und das übergeordnete Element enthält, die mit dem Selektor ( this) übereinstimmen . Hier können Sie die Dinge erledigen, die Sie benötigen, nachdem Sie auf das Element geklickt haben.

Ich habe mich noch nicht entschieden, was besser ist, das if-elseoder dasswitch

var _case = function(evt, selector, cb) {
  var _this = evt.target.closest(selector);
  if (_this && _this.nodeType) {
    cb.call(_this, evt);
    return true;
  } else { return false; }
}

document.getElementById('ifelse').addEventListener('click', function(evt) {
  if (_case(evt, '.parent1', function(evt) {
      console.log('1: ', this, evt.target);
    })) return false;

  if (_case(evt, '.parent2', function(evt) {
      console.log('2: ', this, evt.target);
    })) return false;

  console.log('ifelse: ', this);
})


document.getElementById('switch').addEventListener('click', function(evt) {
  switch (true) {
    case _case(evt, '.parent3', function(evt) {
      console.log('3: ', this, evt.target);
    }): break;
    case _case(evt, '.parent4', function(evt) {
      console.log('4: ', this, evt.target);
    }): break;
    default:
      console.log('switch: ', this);
      break;
  }
})
#ifelse {
  background: red;
  height: 100px;
}
#switch {
  background: yellow;
  height: 100px;
}
<div id="ifelse">
  <div class="parent1">
    <div class="child1">Click me 1!</div>
  </div>
  <div class="parent2">
    <div class="child2">Click me 2!</div>
  </div>
</div>

<div id="switch">
  <div class="parent3">
    <div class="child3">Click me 3!</div>
  </div>
  <div class="parent4">
    <div class="child4">Click me 4!</div>
  </div>
</div>

Ich hoffe es hilft!

ssten
quelle
0

Ich habe festgestellt, dass die von jillykate veröffentlichte Lösung funktioniert, aber nur, wenn das Zielelement am meisten verschachtelt ist. Ist dies nicht der Fall, kann dies durch Iteration über die Eltern behoben werden, d. H.

function on_window_click(event)
{
    let e = event.target;

    while (e !== null)
    {
        // --- Handle clicks here, e.g. ---
        if (e.getAttribute(`data-say_hello`))
        {
            console.log("Hello, world!");
        }

        e = e.parentElement;
    }
}

window.addEventListener("click", on_window_click);

Beachten Sie auch, dass wir Ereignisse mit jedem Attribut behandeln oder unseren Listener auf jeder Ebene anhängen können. Der obige Code verwendet ein benutzerdefiniertes Attribut und window. Ich bezweifle, dass es einen pragmatischen Unterschied zwischen den verschiedenen Methoden gibt.

cz
quelle