У меня есть список якорей с внутренними идентификаторами, и я хочу выбрать все цели, используя .querySelectorAll()
, например:
const targets = document.querySelectorAll('#target-1, #target-2, …')
Для подготовки этого запроса я использовал reduce
:
anchors.reduce((previous, current, index) =>
index === 0 ?
current.getAttribute('href') :
previous + ', ' + current.getAttribute('href')
)
Это почти работает, но есть странная проблема, и результат выглядит так:
https://full-url.com/#target-1, #target-2, #target-3
Когда я только бегаю:
targets[0].getAttribute('href')
… Возвращает ожидаемый результат:
target-1
Можете попробовать сами:
const anchors = Array.from(document.querySelectorAll('a'));
console.info(anchors[0].getAttribute('href'));
console.info(anchors.reduce((previous, current, index) => index === 0 ? current.getAttribute('href') : previous + ', ' + current.getAttribute('href')));
<a href = "#target-1"></a>
<a href = "#target-2"></a>
<a href = "#target-3"></a>
Это происходит, по крайней мере, в Chrome, Safari и Firefox на macOS.
Почему к первому элементу добавляется полный URL-адрес?
Это происходит потому, что reduce
требует передачи начального значения. Попробуйте передать пустую строку:
const anchors = Array.from(document.querySelectorAll('a'));
const first = anchors[0].getAttribute('href');
const all = anchors.reduce((previous, current, index) => index === 0 ? current.getAttribute('href') : previous + ', ' + current.getAttribute('href'), "");
/* The first 'href' value. */
console.info(first);
/* All 'href' values. */
console.info(all);
/* When concatenating an <a> element with a string, a link is returned. */
console.info(anchors[0] + "");
<a href = "#target-1"></a>
<a href = "#target-2"></a>
<a href = "#target-3"></a>
Если вы не передаете начальное значение, используется первый элемент массива. Поскольку элементы в вашем массиве являются элементами <a>
, весь URL-адрес добавляется в начало, потому что, когда вы объединяете элемент привязки со строкой, элемент привязки преобразуется в фактическую ссылку (как описано здесь).
Хорошо, но почему кажется, что в качестве начального значения используется полный URL-адрес?
Ах, понятно - я упустил изначальную ценность. Также хорошее объяснение, почему значение по умолчанию не оценивается как ''
. Спасибо.
Вы должны передать пустую строку в качестве начального значения функции обратного вызова:
let anchors = Array.from(document.querySelectorAll('a'));
anchors = anchors.reduce((previous, current, index) => index === 0 ? current.getAttribute('href') : previous + ', ' + current.getAttribute('href'),"");
console.info(anchors)
<a href = "#target-1"></a>
<a href = "#target-2"></a>
<a href = "#target-3"></a>
Я согласен, что они должны это сделать, но объяснение того, что происходит, может быть хорошим дополнением.
Если немного изменить код, он становится меньше кода и работает. Для этого не обязательно использовать reduce
. Таким образом, вы не возитесь с начальным и накопленным значением, просто преобразуйте якоря списка в список имеющихся href
.
и присоединяйтесь к этому ...
const anchors = Array.from(document.querySelectorAll('a'));
const str = anchors.map(a => a.getAttribute('href')).join(',');
console.info(str);
<a href = "#target-1"></a>
<a href = "#target-2"></a>
<a href = "#target-3"></a>
Спасибо, я согласился с этим кодом, так как его легче поддерживать. : D
Это происходит потому, что initialValue в сокращении является необязательным, если он не передан, он принимает первый индекс массива в качестве начального значения. Итак, если вы зарегистрируете первый индекс и toString его, вы увидите полный URL-адрес вместе с хешем. Пытаться
console.info(anchors[0].toString())
Вот что сказано в спецификации:
Value to use as the first argument to the first call of the callback. If no initial value is supplied, the first element in the array will be used. Calling reduce() on an empty array without an initial value is an error.
Правильный код см. В Ответ ангела.
Я согласен, что они должны это сделать, но объяснение того, что происходит, может быть хорошим дополнением.