I want to select all list items with the name 'viewer'
.
This returns an empty NodeList
:
document.querySelectorAll('[name="viewer"]');
But this returns an array containing the correct items:
[...document.querySelectorAll('li')].filter(({ name }) => name == 'viewer');
The list items were created like this:
const listItem = document.createElement('li');
listItem.name = 'viewer';
listItem.id = 'viewer-1337';
When I inspect them in the console, I see their id
in their HTML but not their name
. For example
<li id="viewer-1337">foo</li>
It seems they do have the name
property set, but it's not accessible via document.querySelectorAll
or visible in their HTML.
Why not?
const names = ['foo', 'bar'];
names.forEach(name => {
const li = document.createElement('li');
li.id = 'viewer-' + name;
li.name = 'viewer';
li.innerHTML = name;
const ul = document.querySelector('ul');
ul.appendChild(li);
});
const nodeList = document.querySelectorAll('[name="viewer"]');
console.log([...nodeList]);
const array = [...document.querySelectorAll('li')].filter(({ name }) => name == 'viewer');
console.log(array);
<ul></ul>
This is occurring because the name
attribute is reserved:
name gets or sets the name property of an element in the DOM. It only applies to the following elements:
<a>, <applet>, <button>, <form>, <frame>, <iframe>, <img>, <input>, <map>, <meta>, <object>, <param>, <select>, and <textarea>.
When you set the name
property of an element which is not one of those types, the element will not receive that value as an attribute value. It'll still be put onto the name
property of the JavaScript object, but it won't be put into the attribute.
const div = document.createElement('div');
console.log(div.attributes.length);
div.name = 'foo';
console.log(div.attributes.length);
console.log(div.hasOwnProperty('name'));
So the query string [name="viewer"]
does not select the elements (because they do not have the name
attribute), but [...document.querySelectorAll('li')].filter(({ name }) => name == 'viewer')
works because the filter
callback is successfully retrieving the name
property from the JavaScript representation of the element.
You can fix it by putting it into the dataset instead:
const names = ['foo', 'bar'];
names.forEach(name => {
const li = document.createElement('li');
li.id = 'viewer-' + name;
li.dataset.name = 'viewer';
li.innerHTML = name;
const ul = document.querySelector('ul');
ul.appendChild(li);
});
const nodeList = document.querySelectorAll('[data-name="viewer"]');
console.log(nodeList.length);
<ul></ul>