4

At the moment I'm doing the following when the document is complete.

var passwordInputs = document.querySelectorAll("input[type=password]");

for (index = 0; index < passwordInputs.length; ++index) {
    passwordInputs[index].addEventListener("focusin", activeWordsFocusIn);
    passwordInputs[index].addEventListener("focusout", activeWordsFocusOut);
}

Which works as expected. However if the page has some additional script which modifies the DOM and adds more input elements then they are not hooked.

How can add event handlers for ALL input elements, even those added to the DOM thru script/ajax?

Not a duplicate I don't consider this a duplicate as this question Detect Changes in the DOM which focuses on detecting changes in the DOM. My questions focus is on adding an eventListener to all input elements even when the DOM changes. I have since added my own answer to this now.

2 Answers2

9

You can use event delegation to add an event handler to the container of the inputs. When an element inside the container triggers an event, we check if the element matches the selector, and if so, the event handler is called.

const delegate = (selector) => (cb) => (e) => e.target.matches(selector) && cb(e);

const inputDelegate = delegate('input[type=password]');

container.addEventListener('focusin', inputDelegate((el) => console.log('focus in', el.target.name)));

container.addEventListener('focusout', inputDelegate((el) => console.log('focus out', el.target.name)));

setTimeout(() => {
  const input = document.createElement('input');
  input.setAttribute('type', 'password');
  input.setAttribute('name', 'input2');
  
  container.append(input);
}, 2000);
<div id="container">
  <input type="password" name="input1">
</div>
Ori Drori
  • 183,571
  • 29
  • 224
  • 209
  • clean solution thanks - does chrome support `matches`? –  Dec 22 '17 at 20:42
  • Yep. It's actually has a very good support. It's even supported by IE9 (with a prefix). – Ori Drori Dec 22 '17 at 20:56
  • I've not seen code snippets in stackoverflow before ... what is the `container` object? –  Dec 22 '17 at 21:48
  • There isn't a container object. It's a reference to the div with the id of `container`. When you use an `id` in html, a global variable to element with the id is created. To make a long story short - it's like an automatic `getElementById()`, and a very good reason not to use ids. – Ori Drori Dec 22 '17 at 21:50
  • got it ... wondering of any advantages over the answer I suggested. Yours looks cleaner, anything else? –  Dec 22 '17 at 21:54
  • I was aiming for generic and reusable using partial application. The selector is external, so you use it for different elements. Because of the partial application, after entering a selector, you can a function you can use for multiple by supplying a callback for each. – Ori Drori Dec 22 '17 at 21:57
2

This is the solution I have found that works...

function activeWordsFocusIn(e) {
    if (isPassword(e)) {
        console.log("focus in");
    }
}

function activeWordsFocusOut(e) {
    if (isPassword(e)) {
        console.log("focus out");
    }
}

function isPassword(e) {
    var e = window.e || e;

    if (e.target.tagName !== 'INPUT')
        return false;

    if (e.target.type !== 'password')
        return false;

    return true;
}

document.addEventListener('focusin', activeWordsFocusIn, false);
document.addEventListener('focusout', activeWordsFocusOut, false);