I have a list of anchors with internal IDs and I want to select all targets using .querySelectorAll()
like:
const targets = document.querySelectorAll('#target-1, #target-2, …')
To prepare this query I used reduce
:
anchors.reduce((previous, current, index) =>
index === 0 ?
current.getAttribute('href') :
previous + ', ' + current.getAttribute('href')
)
This almost works, but there's a strange issue and the result looks like this:
https://full-url.com/#target-1, #target-2, #target-3
When I only run:
targets[0].getAttribute('href')
… it returns the expected result:
target-1
You can try it yourself:
const anchors = Array.from(document.querySelectorAll('a'));
console.log(anchors[0].getAttribute('href'));
console.log(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>
This happens at least in Chrome, Safari and Firefox on macOS.
Why is the full URL prepended to the first element?
This happens because reduce
requires an initial value to be passed. Try passing the empty string:
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.log(first);
/* All 'href' values. */
console.log(all);
/* When concatenating an <a> element with a string, a link is returned. */
console.log(anchors[0] + "");
<a href="#target-1"></a>
<a href="#target-2"></a>
<a href="#target-3"></a>
If you don't pass an initial value, the first element of the array is used. Since the elements in your array are <a>
elements, the entire URL is prepended, because, when you concatenate an anchor element with a string, the anchor element is translated to an actual link (as described here).