0

I'm looking for the simplest way to handle different events on dynamically generated input fields.

Using MutationObserver looks complicated because requires processing each MutationRecord from the list, additional search for input fields and adding or removing listener.

Is there an alternative to MutationObserver like some hack with HTMLCollection:

const inputsHTMLCollection = document.getElementsByTagName('inputs');
// some way for detecting inputsHTMLCollection changing

UPD

I am looking for alternative solutions in pure JS, without using any libraries.

// emulate code on the page
addinput.addEventListener('click', () => {
    const i = document.createElement('input');
    i.type = "text";
    inputs.append(i);
});

const log = (e) => {
  const event = document.createElement('div');
  event.innerText = `${e.type}: on ${e.target.tagName}`;
  events.prepend(event);
};


const inputsHTMLCollection = document.getElementsByTagName('input');
for (let i of inputsHTMLCollection) {
  i.addEventListener('focus', log);
};

// After addinput click - the inputsHTMLCollection length will be changed
// some code, for detect length change here
body {
  display: flex;
}

body > div {
  flex: 0 0 50%;
}

input {
  display: block;
}
<div id="inputs">
  <button id='addinput'>Add input field</button>
  <input type="text" />
</div>
<div id="events"></div>

There is no problems if an event bubbles (for click or input events), but what is the best way to handle focus events in dynamically added inputs?

I don't have access to the code that adds the fields to the page. All I have is the ability to add JS code after the page is loaded.

Kirill
  • 161
  • 2
  • 15
  • 2
    What exactly is changing? Please post a [mcve]. It is right now very hard to make suggestions. Normally you would look at delegation - if so it is a [dupe](https://stackoverflow.com/questions/203198/event-binding-on-dynamically-created-elements) – mplungjan Jun 01 '23 at 08:06
  • or [dupe](https://stackoverflow.com/questions/34896106/attach-event-to-dynamic-elements-in-javascript) – mplungjan Jun 01 '23 at 08:10
  • Thanks, but it's seems like a duple of [this question](https://stackoverflow.com/questions/22062602/htmlcollection-change-event-with-javascript) – Kirill Jun 05 '23 at 06:11
  • If it is a dupe, feel free to delete the question – mplungjan Jun 05 '23 at 06:34

2 Answers2

1

What you need is event delegation.

This is similar to this and this but those questions do not touch on events that do not bubble, like the focus event.

To implement event delegation on events that do not bubble, you will need to catch it during event capturing phase. This can be done by using the third argument of the .addEventListener method:

document.addEventListener('focus', function(event){ ... }, true);
/* OR */
document.addEventListener('focus', function(event){ ... }, { capture: true });

For the focus event specifically, you can also use the focusin event. One of the differences between focus and focusin is that focusin bubbles:

document.addEventListener('focusin', function(event){ ... });

Event delegation for focus event is discussed in this MDN article.

document.addEventListener('focus', (e) => console.log('focus', 'useCapture'), true);
document.addEventListener('focus', (e) => console.log('focus', '{ capture: true }'), { capture: true });
document.addEventListener('focusin', (e) => console.log('focusin'));
<input>
yqlim
  • 6,898
  • 3
  • 19
  • 43
0

If an event bubbles you can catch it at the document:

// emulate code on the page

document.querySelector('button').addEventListener('click', e => {

  e.target.remove();

  document.querySelector('.container').innerHTML = `
    <input id="Text">
    <button class="btn">Auto click me</button>
    <div class="log"></div>
  `;

  const textbox = document.querySelector('#Text');
  textbox.focus();

  document.querySelector('.btn').addEventListener('click', () => {
    document.querySelector('.log').innerHTML += `<div>${textbox.value.trim()}</div>`;
    textbox.value = '';
  });
  
});

// your code, watch for inputs to change

document.addEventListener('input', e => {

  const button = document.querySelector('.btn');

  e.target.value.trim().length === 5 && setTimeout(() => button.click(), 100);

});
<button>Add elements</button>
<div class="container"></div>
Alexander Nenashev
  • 8,775
  • 2
  • 6
  • 17