Search code examples
javascriptdomindexofgetelementsbytagname

Using indexOf for HTML selection elements via getElementsByTagName


Attempting to remove items shown in selects from an array. Here's the JSFiddle

var allProbs = []; // This is defined elsewhere
var dropProbs = [];
var j;
var sel = document.getElementsByTagName("select")
var selarray = Array.prototype.slice.call(sel); // Convert nodelist to array
for (j = 0; j < allProbs.length; j++) {
    alert(allProbs[j] + " " + selarray.indexOf(allProbs[j]) ); // all -1 here???
    if (selarray.indexOf(allProbs[j]) == -1) {
        dropProbs[dropProbs.length] = allProbs[j]
    }
}

Solution

  • Array.prototype.indexOf performs a strict equality after calling toString on the value to compare. You are dealing with host (DOM) objects, the result of calling their toString method (if they have one) is implementation dependent.

    Have you tried:

    console.log(typeof selarray[j].toString);
    

    and if the result of that is function, calling it may return something like:

    "[object HTMLSelectElement]"
    

    or

    "object"
    

    or whatever else the browser developers thought was a good idea. But it will likely return the same value for every member of selarray.

    You haven't shown what the values of allProbs are, but unless they are an exact string match for whatever selarray[j].toString() returns, you'll get -1, and if it does match, you'll always get 0 (i.e. the first one).

    Edit

    So based on values in allProbs being strings, you will never get a match as you are effectively looking for where:

    allProbs[i] === selarray[j].toString(); 
    

    where selarray[j].toString() always returns one of the values above (or something else).

    You may be able to use Array.prototype.findIndex (new method in ECMAScript 2015 so a polyfill may be required), which is like indexOf but uses a function to determine the match.

    Presumably you are trying to find the select element with a value that is equal to a member of allProbs, so you many need something like:

    var index;
    for (var j = 0; j < allProbs.length; j++) {
      index = selarray.findIndex(function(sel) {
        return sel.value == allProbs[j];
      })
    
      /* do stuff based on value of index */
    
    }
    

    Here's a working example:

    // Polyfill for Array.prototype.findIndex
    if (!Array.prototype.findIndex) {
      Array.prototype.findIndex = function(predicate) {
        if (this === null) {
          throw new TypeError('Array.prototype.findIndex called on null or undefined');
        }
        if (typeof predicate !== 'function') {
          throw new TypeError('predicate must be a function');
        }
        var list = Object(this);
        var length = list.length >>> 0;
        var thisArg = arguments[1];
        var value;
    
        for (var i = 0; i < length; i++) {
          value = list[i];
          if (predicate.call(thisArg, value, i, list)) {
            return i;
          }
        }
        return -1;
      };
    }
    
    var allProbs = ["aaa","bbb","ccc"];
    var dropProbs = [];
    var sel = document.getElementsByTagName("select");
    var selarray = Array.prototype.slice.call(sel);
    
    for (var index, j = 0; j < allProbs.length; j++) {
    
        index = selarray.findIndex(function(sel){
          return sel.value == allProbs[j]
        });
    
    
        if (index == -1) {
            dropProbs[dropProbs.length] = allProbs[j];
        }
    }
    document.write('<br>dropProbs: ' + JSON.stringify(dropProbs));
        <select id="sel">
            <option value="aaa" selected>aaa</option>
        </select>