When looking at the ECMAScript compatibility table, it says that Edge 15 and Edge 16 support for ... of loops.
However, when I run this code:
const list = document.querySelectorAll('[data-test]');
console.log(list);
for (const item of list) {
console.log(item);
}
<div data-test></div>
<div data-test></div>
<div data-test></div>
<div data-test></div>
<div data-test></div>
It works in Chrome and Firefox, but not in Edge. Instead it says:
Object doesn't support property or method 'Symbol.iterator'.
As I understand it, NodeList actually should support it, right?
Here's a fildde to try it yourself: Test it online
Can somebody explain the problem or the mistake here?
Edge does support for... of.
It would seem that it doesn't support iterators on NodeLists. Not all array-like objects support iterators and I'm not sure whether there is any standard saying that NodeLists have to.
In any case, it's easy enough to get for ... of to work with them:
const list = document.querySelectorAll('[data-test]');
for (const item of Array.from(list)) {
console.log(item);
}
<div data-test>a</div>
<div data-test>b</div>
<div data-test>c</div>
<div data-test>d</div>
<div data-test>e</div>
If for..of is supported but Edge 15 forgot to add the behavior to NodeList, you could polyfill it yourself with very little code:
NodeList.prototype[Symbol.iterator] = function* () {
for(var i = 0; i < this.length ; i++) {
yield this[i]
}
}
To answer the other question (is it defined as iterable in the spec?) the answer is yes:
The DOM spec defines NodeList as:
interface NodeList {
getter Node? item(unsigned long index);
readonly attribute unsigned long length;
iterable<Node>;
};
Note the third property, iterable<Node>;. Looking it up in the WebIDL spec:
In the ECMAScript language binding, an interface that is iterable will
have “entries”, “forEach”, “keys”, “values” and ##iterator properties
on its interface prototype object.
It seems that Edge doesn't implement any of that.
With ES6 taking ahold, I'm eager to drop jQuery and use native JS for my website builds keeping them quick and lightweight. Also to improve my JS skills as I'm one of those whose jumped straight in with jQuery.
I'm building a tiny tiny library to make the more common used javascript in a function to keep the files small.
function $(elm) {
var elm = document.querySelectorAll(elm);
this.forEach = function(f) {
[].forEach.call(elm, f);
}
return elm;
}
function slider() {
$(".slider").forEach(function() {
alert("Hello");
});
}
slider();
This works great in Chrome etc.. but in IE10/11 I'm getting the error
Object doesn't support this property or method "forEach"
referring to the $(".slider").forEach
Any ideas?
You're adding forEach to the window object, not to the object you return; you're calling $ as a function, not a constructor. Since you're using loose mode (apparently), this within the function call is a reference to the global object (also accessible as window on browsers). You're returning the collection from querySelectorAll unchanged.
The reason it works on Chrome is that the collection returned by querySelectorAll has its own forEach (this is a fairly recent addition).
For this to work, four options:
Return an object and add forEach to it, copying the elements from the QSA collection to that object. E.g.:
function $(selector) {
const result = Array.from(document.querySelectorAll(selector));
result.forEach = Array.prototype.forEach;
// Perhaps map, filter, etc.; add in a loop?
return result;
}
Or in ES5:
var $ = (function() {
var methods = Array.prototype;
function $(selector) {
var result = methods.slice.call(document.querySelectorAll(selector));
result.forEach = methods.forEach;
// Perhaps map, filter, etc.; add in a loop?
return result;
}
return $;
})();
Add forEach to the NodeList prototype if it's not already there and use forEach directly on the collection from querySelectorAll. For instance:
if (typeof NodeList !== "undefined" &&
NodeList.prototype &&
!NodeList.prototype.forEach) {
// Yes, direct assignment is fine here, no need for `Object.defineProperty`
NodeList.prototype.forEach = Array.prototype.forEach;
}
(No need for Object.defineProperty above, enumerable [surprisingly], configurable, and writable are all true for it on Chrome and Firefox, so we can just do direct assignment as above.)
...and then of course your $ becomes nothing more than
function $(selector) {
return document.querySelectorAll(selector);
}
(To start with. If you wanted to add more features, you'd probably want to go the way of #1.)
Return an array:
function $(selector) {
return Array.from(document.querySelectorAll(selector));
}
Or in ES5:
function $(selector) {
return Array.prototype.slice.call(document.querySelectorAll(selector));
}
Subclass Array (which cannot be perfectly polyfilled on pre-ES2015 JavaScript engines) so you can add your own features on top of Array's features:
class MyThingy extends Array {
// Perhaps further methods here
}
function $(selector) {
return MyThingy.from(document.querySelectorAll(selector));
}
No ES5 option here, you'd need to at least transpile and polyfill.
If you're going to add features beyond those provided by Array, I quite like #4 other than the polyfilling available only being "so" good. It may well be sufficient for your purposes.
I'm seeing something weird when I try to include the ember.js library (ember-1.0.0-rc.7.js).
The javacode I have just prints out a javascript array:
<script type="application/javascript">
var songs = [ 'a','b','c'];
console.debug(songs.toString());
for(key in songs)
{
console.debug(songs[key]);
}
</script>
When I don't include the library, it'll print out a , b, c in the console. However, when i do include it, it start printing out a, b, c, but as well as all the functions to...
Example:
function (idx) {
return this.objectAt(idx);
}
function superWrapper() {
var ret, sup = this._super;
this._super = superFunc || K;
ret = func.apply(this, arguments);
this._super = sup;
return ret;
}
function (key) {
return this.mapProperty(key);
}
Any reason why this occurs with the ember.js library, and how do I resolve this issue?
Any advice appreciated,
Thanks,
D
By default, Ember extends built-in prototypes such as Array.prototype to provide extra methods or shim ES5 methods for non-supporting browsers. You are seeing these methods because for...in iterates over the enumerable properties of an object. These include all properties, even those inherited through the prototype chain.
Instead, you should use a regular for loop to iterate over an array:
for(var i=0; i<songs.length; i++) {
console.debug(songs[i]);
}
This will only ever go over actual array elements, i.e. properties with a numerical key. There are nicer ways though, for example using ES5 Array.forEach (shimmed by Ember in older browsers):
songs.forEach(function(song, i) {
console.debug(song);
});
Optionally, you can disable Ember's prototype extension by configuring Ember.EXTEND_PROPERTIES if you're not planning to use them or if they might conflict with other libraries/scripts. There's a whole page dedicated to this issue in the Ember documentation.
embjer.js must be adding functions to the native Array.prototype. You can check if each key is actually a property on the array itself, and not an inherited property, using hasOwnProperty:
for(key in songs) {
if(songs.hasOwnProperty(key)){
console.debug(songs[key]);
}
}
But, it is usually recommended to always use ordinary for loops with arrays, since they have numerical keys:
for(var i = 0; i < songs.length; i++) {
console.debug(songs[i]);
}
You can also use the forEach function that, ironically, Ember added to the Array prototype if it didn't already exist (in older browsers).
songs.forEach(function(song){
console.debug(song);
});
Demo
I have defined prototype for Array indexOf ( to support array indexOf in Internet Explorer )
if(!Array.prototype.indexOf){
Array.prototype.indexOf = function(obj){
for(var i=0; i<this.length; i++){
if(this[i]==obj){
return i;
}
}
return -1;
}
}
When I am creating array with values [1,2,3], this indexOf code snippet added into the Array like below
["1","2","3",function(obj){for(var i=0;i<this.length;i++){if(this[i]==obj){return i;}}return -1;}]
This problem happens only in IE.
Can anyone help me to resolve this issue. Thanks in advance.
I didn't use for...in loop anywhere, for this I am using jQuery sortable toArray method .sortable("toArray");.
I am assuming that at some point you are using a for...in loop to iterate over the elements of your array. For example:
for (var elem in myArray) {
//Do stuff...
}
A for...in loop will enumerate all enumerable properties of an object, including those it has inherited from it's ancestors in its prototype chain. You've added a method to the Array prototype:
Array.prototype.indexOf = function(obj){ //...
This property is enumerable (you can't define non-enumerable properties - see Object.defineProperty - in older versions of IE), so a for...in loop will include this property.
The simple solution is to never use a for...in loop to iterate over an array! Use a normal for loop instead.
Your problem is that your new indexOf() method is marked "enumerable." Unfortunately, there is no fix for this in IE7 or non-standards-mode IE8. But you can at least patch the issue for standards mode IE8 by using some ES5 trickery. Modify your code to look like this and it should get rid of the extra element if you're in IE8 standards mode:
(function () {
var indexOfFn = function(obj){
for(var i=0; i<this.length; i++){
if(this[i]==obj){
return i;
}
}
return -1;
};
if(!Array.prototype.indexOf){
if(typeof Object.defineProperty === "function") {
Object.defineProperty(Array.prototype, "indexOf", {
value: indexOfFn,
enumerable: false
});
} else {
Array.prototype.indexOf = indexOfFn;
}
}
}());
I know it's wordy, so it might not be worth the effort. But it will also protect your JavaScript from other people's bad coding where they might end up using an Array with a for-in loop.
It's quite simple, you're array now contains 4 elements, of which the forth is a function object, you're not defining a new method for the array object, let alone all array objects. Just paste the first snippet at the very top of your script, then:
var foo = [1,2,3];
alert(foo.indexOf(2));//alerts 1
Think of Array.prototype as the template of every array. Whenever you try to access some property or method of an array, that isn't defined, rather then throwing errors, JS will first check the Array.prototype if that object doesn't have that method/property. If it does, JS will use that code, and apply it to the array that initially called it. In the above example foo.indexOf(2) could have been written as Array.prototype.indexOf.apply(foo,[2]);. In other words: JS automatically applied the function of the prototype to foo.
Your "full" code should look like this:
if(!Array.prototype.indexOf)
{
Array.prototype.indexOf = function(obj)
{
for(var i=0; i<this.length; i++)
{
if(this[i] === obj)//preferable, use strict comparison
{
return i;
}
}
return -1;
};
}
var yourArray = [1,2,3,4,'4'];
alert(yourArray.indexOf(4));//alerts 3
alert(yourArray.indexOf('4'));//alerts 4 when using strict comparison, if not, alerts 3
Here's a fiddle, checked in IE8, and it's working just fine
Just google prototypal inheritance and prototype chains or augmenting prototypes in JS and the like, read up and be baffled! ;)
In SQL we can see if a string is in a list like so:
Column IN ('a', 'b', 'c')
What's a good way to do this in JavaScript? It's so clunky to do this:
if (expression1 || expression2 || str === 'a' || str === 'b' || str === 'c') {
// do something
}
And I'm not sure about the performance or clarity of this:
if (expression1 || expression2 || {a:1, b:1, c:1}[str]) {
// do something
}
Or one could use the switch function:
var str = 'a',
flag = false;
switch (str) {
case 'a':
case 'b':
case 'c':
flag = true;
default:
}
if (expression1 || expression2 || flag) {
// do something
}
But that is a horrible mess. Any ideas?
In this case, I have to use Internet Explorer 7 as it's for a corporate intranet page. So ['a', 'b', 'c'].indexOf(str) !== -1 won't work natively without some syntax sugar.
ES6 (ES2015) and up
If you're using ECMAScript 6 (a.k.a. ES2015) or higher, the cleanest way is to construct an array of the items and use Array.includes:
['a', 'b', 'c'].includes('b')
This has some inherent benefits over indexOf because it can properly test for the presence of NaN in the list, and can match missing array elements such as the middle one in [1, , 2] to undefined. It also treats +0 and -0 as equal. includes also works on JavaScript typed arrays such as Uint8Array.
If you're concerned about browser support (such as for IE or Edge), you can check Array.includes at CanIUse.Com, and if you want to target a browser or browser version that's missing includes, you'll need to transpile to a lower ECMAScript version using a tool such as Babel, or include a polyfill script in the browser, such as those available at polyfill.io.
Higher Performance
Note that there is no guarantee that Array.includes() execution time won't scale with the number of elements in the array: it can have performance O(n). If you need higher performance, and won't be constructing the set of items repeatedly (but will be repeatedly checking if the items contain some element), you should use a Set because the ES spec requires implementations of Set (and Map as well) to be sub-linear for reads:
The specification requires sets to be implemented "that, on average, provide access times that are sublinear on the number of elements in the collection". Therefore, it could be represented internally as a hash table (with O(1) lookup), a search tree (with O(log(N)) lookup), or any other data structure, as long as the complexity is better than O(N).
const interestingItems = new Set(['a', 'b', 'c'])
const isItemInSet = interestingItems.has('b')
Note that you can pass in any iterable item to the Set constructor (anything that supports for...of). You can also convert a Set to an array using Array.from(set) or by spreading it [...set].
Without An Array
This is not really recommended, but you could add a new isInList property to strings as follows:
if (!String.prototype.isInList) {
Object.defineProperty(String.prototype, 'isInList', {
get: () => function(...args) {
let value = this.valueOf();
for (let i = 0, l = args.length; i < l; i += 1) {
if (arguments[i] === value) return true;
}
return false;
}
});
}
Then use it like so:
'fox'.isInList('weasel', 'fox', 'stoat') // true
'fox'.isInList('weasel', 'stoat') // false
You can do the same thing for Number.prototype.
Note that Object.defineProperty cannot be used in IE8 and earlier, or very old versions of other browsers. However, it is a far superior solution to String.prototype.isInList = function() { ... } because using simple assignment like that will create an enumerable property on String.prototype, which is more likely to break code.
Array.indexOf
If you are using a modern browser, indexOf always works. However, for IE8 and earlier you'll need a polyfill.
If indexOf returns -1, the item is not in the list. Be mindful though, that this method will not properly check for NaN, and while it can match an explicit undefined, it can’t match a missing element to undefined as in the array [1, , 2].
Polyfill for indexOf or includes in IE, or any other browser/version lacking support
If you don't want to use a service like polyfill.io as mentioned above, you can always include in your own source code standards-compliant custom polyfills. For example, the CoreJs library has an implementation of indexOf.
In this situation where I had to make a solution for Internet Explorer 7, I "rolled my own" simpler version of the indexOf() function that is not standards-compliant:
if (!Array.prototype.indexOf) {
Array.prototype.indexOf = function(item) {
var i = this.length;
while (i--) {
if (this[i] === item) return i;
}
return -1;
}
}
Notes On Modifying Object Prototypes
However, I don't think modifying String.prototype or Array.prototype is a good strategy long term. Modifying object prototypes in JavaScript can lead to serious bugs. You need to decide whether doing so is safe in your own environment. Of primary note is that iterating an array (when Array.prototype has added properties) with for ... in will return the new function name as one of the keys:
Array.prototype.blah = function() { console.log('blah'); };
let arr = [1, 2, 3];
for (let x in arr) { console.log(x); }
// Result:
0
1
2
blah // Extra member iterated over!
Your code may work now, but the moment someone in the future adds a third-party JavaScript library or plugin that isn't zealously guarding against inherited keys, everything can break.
The old way to avoid that breakage is, during enumeration, to check each value to see if the object actually has it as a non-inherited property with if (arr.hasOwnProperty(x)) and only then work with that x.
The new ES6 ways to avoid this extra-key problem are:
Use of instead of in, for (let x of arr). However, depending on the output target and the exact settings/capabilities of your down-leveling transpiler, this may not be reliable. Plus, unless you can guarantee that all of your code and third-party libraries strictly stick to this method, then for the purposes of this question you'll probably just want to use includes as stated above.
Define your new properties on the prototype using Object.defineProperty(), as this will make the property (by default) non-enumerable. This only truly solves the problem if all the JavaScript libraries or modules you use also do this.
Be Aware of One Last Issue
Last, be aware that while polyfills make sense, and modifying object prototypes is a useful strategy, there can occasionally still be scoping problems with that approach.
In a browser, each distinct document object is its own new global scope, and in browser JS it is possible to create new documents (such as those used for off-screen rendering or to create document fragments) or to get a reference to another page's document object (such as via inter-page communication using a named-target link) so it's possible in certain (rare?) circumstances that object prototypes won't have the methods you expect them to have—though you could always run your polyfills again against the new global objects...
In Node.js, modifying prototypes of global objects may be safe, but modifying the prototypes of non-global, imported objects could lead to breakage if you ever end up with two versions of the same package being required/imported, because imports of the two versions will not expose the same objects, thus won't have the same object prototypes. That is, your code could work fine until a dependency or sub-dependency uses a different version from the one you expect, and without any of your own code changing, a simple npm install or yarn install could trigger this problem. (There are options to deal with this, such as yarn's resolutions property in the package.json, but that's not a good thing to rely on if you have other options.)
You can call indexOf:
if (['a', 'b', 'c'].indexOf(str) >= 0) {
//do something
}
Most of the answers suggest the Array.prototype.indexOf method, the only problem is that it will not work on any IE version before IE9.
As an alternative I leave you two more options that will work on all browsers:
if (/Foo|Bar|Baz/.test(str)) {
// ...
}
if (str.match("Foo|Bar|Baz")) {
// ...
}
Arrays have an indexOf method which can be used to search for strings:
js> a = ['foo', 'bar', 'baz']
foo,bar,baz
js> a.indexOf('bar')
1
js> a.indexOf('quux')
-1
In addition to indexOf (which other posters have suggested), using prototype's Enumerable.include() can make this more neat and concise:
var list = ['a', 'b', 'c'];
if (list.includes(str)) {
// do stuff
}
A trick I've used is
>>> ("something" in {"a string":"", "somthing":"", "another string":""})
false
>>> ("something" in {"a string":"", "something":"", "another string":""})
true
You could do something like
>>> a = ["a string", "something", "another string"];
>>> b = {};
>>> for(var i=0; i<a.length;i++){b[a[i]]="";} /* Transform the array in a dict */
>>> ("something" in b)
true
Using indexOf(it doesn’t work with IE8).
if (['apple', 'cherry', 'orange', 'banana'].indexOf(value) >= 0) {
// found
}
To support IE8, you could implement Mozilla’s indexOf.
if (!Array.prototype.indexOf) {
// indexOf polyfill code here
}
Regular Expressions via String.prototype.match (docs).
if (fruit.match(/^(banana|lemon|mango|pineapple)$/)) {
}
Here's mine:
String.prototype.inList=function(list){
return (Array.apply(null, arguments).indexOf(this.toString()) != -1)
}
var x = 'abc';
if (x.inList('aaa','bbb','abc'))
console.log('yes');
else
console.log('no');
This one is faster if you're OK with passing an array:
String.prototype.inList=function(list){
return (list.indexOf(this.toString()) != -1)
}
var x = 'abc';
if (x.inList(['aaa','bbb','abc']))
console.log('yes')
Here's the jsperf: http://jsperf.com/bmcgin-inlsit
RegExp is universal, but I understand that you're working with arrays. So, check out this approach. I use to use it, and it's very effective and blazing fast!
var str = 'some string with a';
var list = ['a', 'b', 'c'];
var rx = new RegExp(list.join('|'));
rx.test(str);
You can also apply some modifications, i.e.:
One-liner
new RegExp(list.join('|')).test(str);
Case insensitive
var rx = new RegExp(list.join('|').concat('/i'));
And many others!
Looks like you need to use in_array function.
jQuery -> inArray
Prototype -> Array.indexOf
Or, see these examples if you are not using jQuery or Prototype:
http://phpjs.org/functions/in_array:432
http://www.bitrepository.com/equivalent-of-phps-in_array-function.html
http://codingforums.com/showthread.php?t=63796
Stylistic note: variables named thisthing thatthing, should be named to tell you something about what they contain (noun).
Thanks for the question, and the solution using the Array.indexOf method.
I used the code from this solution to create a inList() function that would, IMO, make the writing simpler and the reading clearer:
function inList(psString, psList)
{
var laList = psList.split(',');
var i = laList.length;
while (i--) {
if (laList[i] === psString) return true;
}
return false;
}
USAGE:
if (inList('Houston', 'LA,New York,Houston') {
// THEN do something when your string is in the list
}
My solution results in a syntax like this:
// Checking to see if var 'column' is in array ['a', 'b', 'c']
if (column.isAmong(['a', 'b', 'c']) {
// Do something
}
And I implement this by extending the basic Object prototype, like this:
Object.prototype.isAmong = function (MyArray){
for (var a=0; a<MyArray.length; a++) {
if (this === MyArray[a]) {
return true;
}
}
return false;
}
We might alternatively name the method isInArray (but probably not inArray) or simply isIn.
Advantages: Simple, straightforward, and self-documenting.
I'm surprised no one had mentioned a simple function that takes a string and a list.
function in_list(needle, hay)
{
var i, len;
for (i = 0, len = hay.length; i < len; i++)
{
if (hay[i] == needle) { return true; }
}
return false;
}
var alist = ["test"];
console.log(in_list("test", alist));
A simplified version of SLaks' answer also works:
if ('abcdefghij'.indexOf(str) >= 0) {
// Do something
}
....since strings are sort of arrays themselves. :)
If needed, implement the indexof function for Internet Explorer as described before me.
My little contribution:
function fnListIndexOf(pList, pValue)
{
return pList.split(",").indexOf (pValue);
}
fnListIndexOf("1,2,3,4,5,a,b,c","a")