Why won't the JavaScript .map() function Work in Arrays of HTML Elements?

This is probably due to my ignorance, so I'm hoping some of you JavaScript experts will jump in and point out the error in my ways. :wink:

This was tested in Keyboard Maestro 7.3.1 (7.3.1) on macOS 10.11.4.

I have a very simple JavaScript which fails for:

// FAILS -- ARRAY.MAP --

// -- linkElem is a valid array of HTML Elements --
var linkList2 = linkElem.map(function(x) {
        return (x.textContent + '\t' + x.href);
    });

ERROR: "linkElem.map is not a function"

but works for both of these approaches:

// #1 -- STANDARD FOR LOOP --

var numElements = linkElem.length;
for (var iElem = 0; iElem < numElements; iElem++) {
    linkList.push(linkElem[iElem].textContent + "\t" + linkElem[iElem].href);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// #2 -- ARRAY.PROTOTYPE.SLICE --

var resultsStr  = Array.prototype.slice.call( linkElem )
    .map(function(x) {
        return (x.textContent + '\t' + x.href);
    }).join('\n');

Why?

Am I just using the `.map()` method incorrectly, or is it something else?

My thanks to @ComplexPoint for sharing the above 2nd approach (`Array.prototype.slice.call( linkElem ).map(function(x)`) [in another macro/script.](https://forum.keyboardmaestro.com/t/how-collect-all-matching-links-on-a-browser-page/5438/2?u=jmichaeltx)  I don't understand it, even after studing the [Array.prototype.slice() - JavaScript | MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/slice) reference page.

###Complete Script

'use strict';

(function run() {    // function will auto-run when script is executed

//debugger;


var majorDivElem = document.getElementsByClassName("span16");
var linkElem = majorDivElem[0].getElementsByTagName("a")

var linkList = [];

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//##### THIS WORKS #########

var numElements = linkElem.length;
for (var iElem = 0; iElem < numElements; iElem++) {
    linkList.push(linkElem[iElem].textContent + "\t" + linkElem[iElem].href);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//####### THIS WORKS #######

var resultsStr  = Array.prototype.slice.call( linkElem )
    .map(function(x) {
        return (x.textContent + '\t' + x.href);
    }).join('\n');
    
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//#### THIS DOES NOT WORK ####

//   Error:  linkElem.map is not a function
/*

var linkList2 = linkElem.map(function(x) {
        return (x.textContent + '\t' + x.href);
    });
*/
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//#### THIS DOES NOT WORK ####
//   Error:  linkElem.forEach is not a function
/*
linkElem.forEach(function(oElem) {
    linkList.push(oElem.textContent + "\t" + oElem.href);
  })
*/
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//#### THIS DOES NOT WORK ####

//   Error:  linkElem.map is not a function
/*
linkElem.map(function (oElem) {
    linkList.push(oElem.textContent + "\t" + oElem.href);
    return;
  })
*/


return resultsStr;  // linkList.join("\n");


//⩶⩶⩶⩶⩶⩶⩶⩶⩶⩶⩶⩶⩶⩶⩶⩶⩶⩶⩶⩶⩶⩶⩶⩶⩶⩶⩶⩶⩶⩶
//    FUNCTIONS
//⩶⩶⩶⩶⩶⩶⩶⩶⩶⩶⩶⩶⩶⩶⩶⩶⩶⩶⩶⩶⩶⩶⩶⩶⩶⩶⩶⩶⩶⩶


})();

###MACRO:   @WEB Test JavaScript in Chrome

~~~ VER: 1.0    2016-12-21 ~~~

####DOWNLOAD:
@WEB Test JavaScript in Chrome.kmmacros (6.1 KB)


###ReleaseNotes

This macro is just for testing a concept/approach.


You’re not doing anything wrong - that’s just how it works.

It has something to due with the fact they’re not true arrays, or something like that. They don’t have the “iterator” that’s needed for doing “forEach”.

OK, thanks Dan. Good to know I'm not a total idiot, at least today! :wink:

So, I guess using the Array.prototype.slice.call method somehow converts the HTML array into an array that understands .map() and forEach, correct?

Even so, I'm not sure I see the advantage over using a normal for loop.

I dunno. I might be wrong - I’m probably looking at this from a C#/.NET perspective, and I might not have a clue.

But I do know you’re correct that “forEach” doesn’t work.

OK. I guess I just learned from the school of hard knocks!

1 Like

A DOM Elements Collection is not an Array object (it’s an instance of a different class) but you create an Array containing the data of the collection with Array.prototype.slice

2 Likes

Thanks, Rob. That clears things up, and provides a solution.