How to assign a property (not event) to an element that is created later, outside of NodeList?

57 Views Asked by At
  • When we create the code that assigns this property from a similar element, we can call it as a method.
  • This feature can be read continuously with the change event to the date input.
  • I wanted to try something with MutationObserver, but it seems to me that it is no different from calling it as the method I mentioned at first.
    <div class="inputs">
        <input type="date"/>
    </div>
    <button>Add</button>

document.querySelector('button').addEventListener('click', function () {
    let clone = `<input type="date"/>`;
    document.querySelector('.inputs').insertAdjacentHTML('beforeend', clone);
})
document.querySelectorAll('input[type=date]').forEach(input=>{
  input.max = new Date().toISOString().split("T")[0];
})

But apart from these, is there a method that we can run in a smoother dynamic structure?

1

There are 1 best solutions below

0
Michele Viviani On

In my opinion for this simple operation MutationObserver is too much expensive.

The main problem with your code, is that the document.querySelectorAll('input[type=date]').forEach... is not "reactive", but executed only once, at every script tag/file parsing. Indeed for the first one input, added statically ( or server side ) in your html it works, but for the others added dynamically with the button it won't work.

So you must operate inside the click listener, like you did. But instead of simply create an html string you can create an HTMLInputElement , set wathever property you want directly and append it.

    const inputs = document.querySelector('.inputs');
    /**
     * We can create only once our `dateInput` prototype with all the attributes, 
     * and than clone it each time we need. ( You could use a clojure to, 
     * or put this 3 lines directly in the listener, but this should be a little more efficient)
     **/
    const dateInput = document.createElement('input');
    dateInput.type = 'date';
    dateInput.max = new Date().toISOString().split("T")[0];
    
    document.querySelector('button').addEventListener('click', () =>
        inputs.append(dateInput.cloneNode()));

Now you have to manage the inputs which are yet in the page.

  • If your view is rendered by a server script, you could simply add the max attribute there.

  • If you want to add it via javascript, you can keep this line, and is going to work for multiple elements to:

   document.querySelectorAll('input[type=date]').forEach( input => {
      input.max = new Date().toISOString().split("T")[0];
   });

Working example:

    const inputs = document.querySelector('.inputs');
    const dateInput = document.createElement('input');
    dateInput.type = 'date';
    dateInput.max = new Date().toISOString().split("T")[0];
    
    document.querySelector('button').addEventListener('click', () =>
        inputs.append(dateInput.cloneNode()));
    
    document.querySelectorAll('input[type=date]').forEach(input => {
        input.max = new Date().toISOString().split("T")[0];
    });
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <div class="inputs">
        <input type="date"/>
    </div>
    <button>Add</button>
</body>
</html>

--edit: Another example, with more consistent date value:

    const inputs = document.querySelector('.inputs');
    /**
     * We can create only once our `dateInput` prototype with all the attributes we need, 
     * and than clone it every time we need. ( You could use a clojure to, 
     * or put this 2 lines directly in the listener, but this should be a little more efficient)
     **/
    const dateInput = document.createElement('input');
    dateInput.type = 'date';
    
    document.querySelector('button').addEventListener('click', () => {
        const newNode = dateInput.cloneNode();
        newNode.max = new Date().toISOString().split("T")[0];
        inputs.append(newNode);
    });
    
    document.querySelectorAll('input[type=date]').forEach( input => {
        input.max = new Date().toISOString().split("T")[0];
    });