Search code examples
javascripthtmlhref

Why does getAttribute('href') return the full URL when used with reduce?


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?


Solution

  • 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).