300

If you have worked with JavaScript at any length you are aware that Internet Explorer does not implement the ECMAScript function for Array.prototype.indexOf() [including Internet Explorer 8]. It is not a huge problem, because you can extend the functionality on your page with the following code.

Array.prototype.indexOf = function(obj, start) {
     for (var i = (start || 0), j = this.length; i < j; i++) {
         if (this[i] === obj) { return i; }
     }
     return -1;
}

When should I implement this?

Should I wrap it on all my pages with the following check, which checks if the prototype function exists and if not, go ahead and extend the Array prototype?

if (!Array.prototype.indexOf) {

    // Implement function here

}

Or do browser check and if it is Internet Explorer then just implement it?

//Pseudo-code

if (browser == IE Style Browser) {

     // Implement function here

}
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Bobby Borszich
  • 11,639
  • 9
  • 37
  • 35
  • Actually `Array.prototype.indexOf` is not part of ECMA-262/ECMAScript. See http://www.ecma-international.org/publications/files/ECMA-ST/ECMA-262.pdf Maybe you're thinking `String.prototype.indexOf`... – Crescent Fresh Nov 16 '09 at 19:39
  • 5
    It's an extension, not part of the original standard. It should, however, be implemented as part of Javascript 1.6 (which IE fails to do) https://developer.mozilla.org/en/New_in_JavaScript_1.6 – Josh Stodola Nov 16 '09 at 19:41
  • 1
    @Josh: was just referring to "IE does not implement the ECMAScript function..." – Crescent Fresh Nov 16 '09 at 19:48
  • 4
    Your implementation of `Array.indexOf` doesn't take negative starting indices into account. See Mozilla's suggestion stop-gap implementation here: https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/indexOf – nickf Oct 27 '10 at 15:44
  • 3
    I've updated the question to use "===", because I'm worried people will copy it with the "==" and that would be wrong - other than that it's fine. See Eli Grey's answer. – joshcomley Apr 15 '11 at 19:25
  • Actually, Array.prototype.indexOf is a part of ECMAScript 5 (where the link to the standard now goes): "15.4.4.14 Array.prototype.indexOf ( searchElement [ , fromIndex ] )" – Denis V Nov 28 '13 at 20:37

10 Answers10

215

Do it like this...

if (!Array.prototype.indexOf) {

}

As recommended compatibility by MDC.

In general, browser detection code is a big no-no.

Josh Stodola
  • 81,538
  • 47
  • 180
  • 227
  • I don't have enough rep to edit the question but feel free to remove the ECMAScript lingo and replace with the appropriate wording. Thanks Again – Bobby Borszich Nov 16 '09 at 19:47
  • 12
    Be careful if you use this kind of detection. Another library might implement this function before you test it, and it might not be standards compliant (prototype has done it a while ago). If I were working in a hostile environment (lots of other coders using lots of distinct libraries), I wouldn't trust any of these... – Pablo Cabrera Nov 17 '09 at 11:38
  • The "Linked" column ---> is really handy! I love the answer here: http://stackoverflow.com/questions/1744310/how-to-fix-array-indexof-in-javascript-for-internet-explorer-browsers?lq=1 – gordon Nov 06 '15 at 21:49
  • Does it has to be wrapped in every js file? – rd22 Feb 22 '16 at 10:04
  • Who is MDC exactly? – Ferrybig Jul 04 '18 at 15:12
142

Alternatively, you could use the jQuery 1.2 inArray function, which should work across browsers:

jQuery.inArray( value, array [, fromIndex ] )
Cees Timmerman
  • 17,623
  • 11
  • 91
  • 124
Moses Lee
  • 1,544
  • 1
  • 9
  • 4
  • The 'indexOf' is native code (right), so will the jQuery 'inArray()' be as fast, such as use native when available and poly-fill when not? – Jeach Mar 07 '13 at 17:12
  • 10
    Ok so to answer my own comment (above), I just implemented it and on Chrome it's as fast as when I was using 'indexOf()', but in IE 8 it is very, very slow... so at least we know that 'inArray()' uses native when it can. – Jeach Mar 07 '13 at 17:22
79

The full code then would be this:

if (!Array.prototype.indexOf) {
    Array.prototype.indexOf = function(obj, start) {
         for (var i = (start || 0), j = this.length; i < j; i++) {
             if (this[i] === obj) { return i; }
         }
         return -1;
    }
}

For a really thorough answer and code to this as well as other array functions check out Stack Overflow question Fixing JavaScript Array functions in Internet Explorer (indexOf, forEach, etc.).

Community
  • 1
  • 1
Luis Perez
  • 27,650
  • 10
  • 79
  • 80
  • 2
    thank you for just having the full thing. I visit this page frequently whenever I need cross-platform indexOf in a new project, and your snippet is the only one with full code. :) Those few seconds really add up when one frequents this page. – dylnmc Jun 07 '17 at 21:11
16

The underscore.js library has an indexOf function you can use instead:

_.indexOf([1, 2, 3], 2)
Brad Koch
  • 19,267
  • 19
  • 110
  • 137
scotta7exander
  • 383
  • 4
  • 11
  • 4
    This answer avoids [messing with the array prototype](http://stackoverflow.com/q/780422/425313), and it delegates to the native indexOf when available. I like it. – Brad Koch Oct 18 '12 at 20:08
  • Seems to be the easiest way if you are able to include underscore or lodash – ChrisRich Mar 01 '16 at 23:03
10

You should check if it's not defined using if (!Array.prototype.indexOf).

Also, your implementation of indexOf is not correct. You must use === instead of == in your if (this[i] == obj) statement, otherwise [4,"5"].indexOf(5) would be 1 according to your implementation, which is incorrect.

I recommend you use the implementation on MDC.

Eli Grey
  • 35,104
  • 14
  • 75
  • 93
9

There is Mozilla official solution: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/indexOf

(function() {
    /**Array*/
    // Production steps of ECMA-262, Edition 5, 15.4.4.14
    // Reference: http://es5.github.io/#x15.4.4.14
    if (!Array.prototype.indexOf) {
        Array.prototype.indexOf = function(searchElement, fromIndex) {
            var k;
            // 1. Let O be the result of calling ToObject passing
            //    the this value as the argument.
            if (null === this || undefined === this) {
                throw new TypeError('"this" is null or not defined');
            }
            var O = Object(this);
            // 2. Let lenValue be the result of calling the Get
            //    internal method of O with the argument "length".
            // 3. Let len be ToUint32(lenValue).
            var len = O.length >>> 0;
            // 4. If len is 0, return -1.
            if (len === 0) {
                return -1;
            }
            // 5. If argument fromIndex was passed let n be
            //    ToInteger(fromIndex); else let n be 0.
            var n = +fromIndex || 0;
            if (Math.abs(n) === Infinity) {
                n = 0;
            }
            // 6. If n >= len, return -1.
            if (n >= len) {
                return -1;
            }
            // 7. If n >= 0, then Let k be n.
            // 8. Else, n<0, Let k be len - abs(n).
            //    If k is less than 0, then let k be 0.
            k = Math.max(n >= 0 ? n : len - Math.abs(n), 0);
            // 9. Repeat, while k < len
            while (k < len) {
                // a. Let Pk be ToString(k).
                //   This is implicit for LHS operands of the in operator
                // b. Let kPresent be the result of calling the
                //    HasProperty internal method of O with argument Pk.
                //   This step can be combined with c
                // c. If kPresent is true, then
                //    i.  Let elementK be the result of calling the Get
                //        internal method of O with the argument ToString(k).
                //   ii.  Let same be the result of applying the
                //        Strict Equality Comparison Algorithm to
                //        searchElement and elementK.
                //  iii.  If same is true, return k.
                if (k in O && O[k] === searchElement) {
                    return k;
                }
                k++;
            }
            return -1;
        };
    }
})();
Will V King
  • 131
  • 1
  • 3
  • 1
    Just being pedantic but MDN is not just Mozilla. It is a community driven project that contains Mozilla staff but also volunteers, anyone can join and contribute. – ste2425 Jan 04 '16 at 16:15
5

I would recommend this to anyone looking for missing functionality:

http://code.google.com/p/ddr-ecma5/

It brings in most of the missing ecma5 functionality to older browers :)

Josh Mc
  • 9,911
  • 8
  • 53
  • 66
2

This was my implementation. Essentially, add this before any other scripts on the page. i.e. in your master for a global solution for Internet Explorer 8. I also added in the trim function which seems to be used in allot of frameworks.

<!--[if lte IE 8]>
<script>
    if (!Array.prototype.indexOf) {
        Array.prototype.indexOf = function(obj, start) {
            for (var i = (start || 0), j = this.length; i < j; i++) {
                if (this[i] === obj) {
                    return i;
                }
            }
            return -1;
        };
    }

    if(typeof String.prototype.trim !== 'function') {
        String.prototype.trim = function() {
            return this.replace(/^\s+|\s+$/g, '');
        };
    };
</script>
<![endif]-->
Glennweb
  • 99
  • 5
2

it works for me.

if (!Array.prototype.indexOf) {
  Array.prototype.indexOf = function(elt /*, from*/) {
    var len = this.length >>> 0;

    var from = Number(arguments[1]) || 0;
    from = (from < 0)? Math.ceil(from) : Math.floor(from);
    if (from < 0)
    from += len;

    for (; from < len; from++) {
      if (from in this && this[from] === elt)
        return from;
    }
    return -1;
  };
}
Allen Wong
  • 1,162
  • 1
  • 10
  • 15
1

With the Underscore.js

var arr=['a','a1','b'] _.filter(arr, function(a){ return a.indexOf('a') > -1; })

srikanth_yarram
  • 957
  • 9
  • 16