Разверните итеративный элемент или не повторяемый элемент в массив без проверки элемента.length

Дано html

<div></div>
<div></div>

призвание document.querySelector("div") возвращает первое div элемент, где .length не является свойством возвращаемого значения.

призвание document.querySelectorAll() возвращает NodeList иметь .length имущество.

Разница между двумя возвращаемыми значениями .querySelector() а также .querySelectorAll() является то, что первое не является итеративным; и ошибка будет выдана при попытке использовать spread element развернуть элемент в массив.

В следующих примерах учтите, что либо div или же divs это параметр, полученный в теле вызова функции. Таким образом, насколько это возможно, невозможно определить, была ли переменная определена в результате Element.querySelector(), Element.querySelectorAll(), document.querySelector() или же document.querySelectorAll(); дальше разница между .querySelector() а также .querySelectorAll() можно проверить только с помощью .length,

var div = document.querySelector("div");
for (let el of div) {
  console.log(".querySelector():", el)
}
<div></div>
<div></div>

бревна

Uncaught TypeError: div[Symbol.iterator] is not a function

в то время как

var div = document.querySelectorAll("div");
for (let el of div) {
  console.log(".querySelectorAll():", el)
}
<div></div>
<div></div>

возвращает ожидаемый результат; то есть, document.querySelectorAll("div") расширяется, чтобы заполнить итеративный массив.

Мы можем получить ожидаемый результат в .querySelector() установив div как элемент Array

[div]

в for..ofiterable параметр.

Самые близкие пришли к использованию одного и того же шаблона для обоих или .querySelector() или же .querySelectorAll() использует callback из Array.from() и .tagName переменной и spread element, Хотя это исключает дополнительные селекторы, которые могли быть вызваны с .querySelector(), например .querySelector("div.abc"),

var div = document.querySelector("div");
var divs = document.querySelectorAll("div");
var elems = Array.from({length:div.length || 1}, function(_, i) {
  return [...div.parentElement.querySelectorAll(
    (div.tagName || div[0].tagName))
         ][i]
});

for (let el of elems) {
  console.log(".querySelector():", el)
}

elems = Array.from({length:divs.length || 1}, function(_, i) {
  return [...divs[0].parentElement.querySelectorAll(
    (divs.tagName || divs[0].tagName))
         ][i]
});

for (let el of elems) {
  console.log("querySelectorAll:", el)
}
<div></div>
<div></div>

Это не обеспечивает адекватной точности по дополнительным причинам; Element.querySelector() мог быть первоначально передан в функцию, а не document.querySelector() аналогично для .querySelectorAll(). Not sure if it is possible to retrieve the exact selector passed to.querySelector, All` без модификации нативной функции?

Требуемый шаблон будет принимать переменную и расширять содержимое итерируемого в массив, если .querySelectorAll() использовался; что бы лечить .getElementsByTagName(), .getElementsByClassName(), .getElementsByTagName(), .getElementsByName() тот же самый; или установите единственное значение, возвращаемое .querySelector() как элемент массива.

Обратите внимание, что текущее рабочее решение

div.length ? div : [div]

который повторяется div если div имеет .length свойство, возможно, повторяемое, хотя просто .length собственность и не быть iterable; еще установить div как отдельный элемент массива, итеративный.

var div = document.querySelector("div");
var divs = document.querySelectorAll("div");
var elems = div.length ? div : [div];

for (let el of elems) {
  console.log(".querySelector():", el)
}

var elems = divs.length ? divs : [divs];

for (let el of elems) {
  console.log("querySelectorAll:", el)
}
<div></div>
<div></div>

Может ли это быть достигнуто

  • без проверки .length переменной?
  • без ссылки на элемент три раза в одной строке?

Может подход рабочего решения

  • быть улучшенным; это должно [Symbol.iterator] из div проверяться вместо .length?
  • есть ли магия, использующая .spread element или же rest element что может позволить пропустить проверку .length объекта?
  • будет использовать Generator, Array.prototype.reduce() или другой подход изменить необходимость проверки .length или же [Symbol.iterator] свойство переменной перед расширением элемента в массив?

Или, является ли приведенный выше подход максимально кратким, учитывая разницу между iterable или нет iterable?

1 ответ

Решение

Я бы сделал более или менее, что Array.from делает, но проверьте тип length вместо того, чтобы всегда преобразовывать это:

const itemsOrSingle = items => {
    const iteratorFn = items[Symbol.iterator]

    if (iteratorFn) {
        return Array.from(iteratorFn.call(items))
    }

    const length = items.length

    if (typeof length !== 'number') {
        return [items]
    }

    const result = []

    for (let i = 0; i < length; i++) {
        result.push(items[i])
    }

    return result
}
Другие вопросы по тегам