JavaScript'teki dinamik öğelere etkinlik ekleyin


92

Dinamik olarak oluşturulan bir listeye html verilerini dinamik olarak eklemeye çalışıyorum, ancak dinamik olarak oluşturulan düğme için bir onclick olayı eklemeye çalıştığımda olay tetiklenmiyor. Çözüm gerçekten takdir edilecektir.

Javascript kodu:

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

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

Html'yi nasıl oluşturuyorsunuz?
jim0thy

1
Olay dinleyicisini eklemeye çalıştığınızda öğe mevcut olmadığı için söyleyebilirim. - buna bir göz atın learn.jquery.com/events/event-delegation
Craicerjack

Yanıtlar:


211

Bu, elemanınızın dinamik olarak yaratılmış olmasından kaynaklanmaktadır. Olayı işlemek için olay yetkilendirmesini kullanmalısınız .

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

jquery bunu kolaylaştırır:

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

İşte olay delegasyonu olay delegasyonu makalesi hakkında bir makale


1
Teşekkür ederim! Bazen saf JS çözümü iphone'da çalışmaz, ancak örneğin başka bir üst div'deki belgeyi değiştirirseniz çalışır. Yetki için en iyi çözüm nedir?
Kholiavko

3
@Kholiavko "Ebeveyn" eleman dinamik olarak oluşturulmadığı sürece çalışmalı diyebilirim. Farklı olay işleyicileri arasında herhangi bir çakışma olmaması için olay işleyicisini dinamik create d öğesinin ilk ebeveynine
bağlardım

1
Ancak bazen iphone'da çalışmaz, hatta "ana" öğe dinamik olarak oluşturulmaz. İşte exapmle tüm tarayıcı üzerinde çalışır, ancak iphone (safary ve hatta krom) üzerinde değildir. Ve nedenini anlamıyorum.
Kholiavko

2
@Kholiavko bunun nedeni codepen'in her şeyi sarmak için bir sandbox kullanmasıdır. Bu kodu gerçekten normal bir sayfada
yazarsanız

2
Beklenmedik tür zorlamasını önlemek için fazladan bir eşittir işareti (===) eklemeyi düşünün
Staccato

19

Tıklamaları yakalayıp document.bodyardından olay hedefini kontrol ederek bir çözüm var .

document.body.addEventListener( 'click', function ( event ) {
  if( event.srcElement.id == 'btnSubmit' ) {
    someFunc();
  };
} );

2
Veya en yakın konteyner
mplungjan

Mükemmel cevap!
Rahul Purohit

WOW bu harika, çok fazla zaman ve kod tasarrufu sağlıyor! :)
Anna Medyukh

3
Not: event.srcElement kullanımdan kaldırılmıştır. Bunun yerine event.target kullanmanız önerilir.
dsanchez

12

Olayı öğelerden sonra eklemelisiniz, örneğin kendinize global bir olay documentdeğil, eklenen öğelere belirli bir olay iliştirmelisiniz .

Örneğin

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>


2
Bu yaklaşım yalnızca CSR (Client Side Rendering) için çalışacaktır. Bu, SSR (Sunucu Tarafı Oluşturma) için çalışmayacaktır. Bunun hem CSR hem de SSR için çalışmasını sağlamak için Çözüm Delegasyonu :javascript document.addEventListener('click',function(e){ if(e.target && e.target.id== 'brnPrepend'){ //do something } });
Yatendra Goel

Ancak performans nedeniyle bu yaklaşım daha iyidir. Yani bu sizin kullanım durumunuza bağlıdır.
R3tep

8

Aradaki fark, DOM’daki öğeleri nasıl oluşturduğunuz ve eklediğinizle ilgilidir.

Üzerinden bir öğe oluşturursanız document.createElement, bir olay dinleyicisi ekleyin ve bunu DOM'a ekleyin. Olaylarınız ateşlenecek.

Şunun gibi bir dize olarak bir öğe oluşturursanız: html + = "<li> test </li>" ", elment teknik olarak sadece bir dizedir. Dizelerin olay dinleyicileri olamaz.

Çözümlerden biri, her bir öğeyi oluşturmak document.createElementve ardından bunları doğrudan bir DOM öğesine eklemektir.

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

3

Buna benzer bir şey yapabilirsiniz:

// 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);

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

Artık olayları şöyle kaydedebilirsiniz:

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

  });

Tarayıcı Desteği

chrome - 4.0, Edge - 9.0, Firefox - 3.5 Safari - 3.2, Opera - 10.0 ve üstü


2

Buna yardımcı olmak için küçük bir kitaplık oluşturdum: GitHub'daki kitaplık kaynağı

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

İşlevsellik jQuery.on () 'a benzer.

Kitaplık , hedef öğeyi verilen seçiciye göre test etmek için Element.matches () yöntemini kullanır . Bir olay tetiklendiğinde, geri arama yalnızca hedef öğe belirtilen seçiciyle eşleşirse çağrılır.


0

Konunun çok eski olduğunu biliyorum ama pure kullanarak iyi ve çok kolay çalışan çok kullanışlı bir kod oluşturmak için kendime birkaç dakika verdim JAVASCRIPT. İşte basit bir örnek içeren kod:

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>


0

Bunun için basit bir işlev yaptım.

_caseFonksiyonu hedefi olsun, ama aynı zamanda olayı bağlamak ana unsuru almak için sadece izin verir.

Geri çağırma işlevi, target ( evt.target) ve selector ( this) ile eşleşen üst öğeyi tutan olayı döndürür . Burada, element tıklandıktan sonra ihtiyacınız olan şeyleri yapabilirsiniz.

Hangisinin daha iyi olduğuna henüz karar vermedim if-else.switch

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>

Umarım yardımcı olur!


0

Jillykate tarafından yayınlanan çözümü buldum , ancak yalnızca hedef öğe en iç içe geçmişse . Durum böyle değilse, üst öğe üzerinde yinelenerek bu düzeltilebilir, örn.

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

Ayrıca olayları herhangi bir özniteliğe göre işleyebileceğimizi veya dinleyicimizi herhangi bir düzeyde ekleyebileceğimizi unutmayın. Yukarıdaki kod, özel bir özellik kullanır ve window. Çeşitli yöntemler arasında herhangi bir pragmatik fark olduğundan şüpheliyim.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.