I am currently creating a website and have some javascript that works in all browsers except IE7 and IE8. I have done some tests on the code by inserting several 'alert' statements and deduced that the javascript breaks at one particular 'if' statement. It is not the code within the 'if' statement either because I have also tested this.
I can't see anything wrong with the actual 'if' statement myself but please let me know if there is a problem with IE7/IE8 and the code I have produced. The code can be seen below.
Thanks in advance for any help.
var Items = new Array("a","b","c","d");
var queryString = window.location.search.substring(1);
if(Items.indexOf(queryString) != "-1"){
//code goes here
}
There's no "indexOf()" function on IE's Array prototype. If there were, it'd return a numeric value and not a string.
You can find an "indexOf()" polyfill at the MDN documentation page for the function.
Also, when you declare and initialize arrays, use array constant notation:
var Items = ["a", "b", "c", "d"];
Here is one way of extending the Array object to support indexOf in those browsers that don't support it. Doing this has its own issues, if you ever iterate an array via for (x in a) (not suggested) and don't check hasOwnProperty this will cause you problems.
if(!Array.indexOf){
Array.prototype.indexOf = function(obj){
for(var i=0; i<this.length; i++){
if(this[i]==obj){
return i;
}
}
}
}
Related
So I am getting incredible weird java script behavior inside a chrome tab. The page is behind a login so I can't post it but can someone explain exactly what is happening???
for(var z in ""){ console.log(z) }
contains
//undefined
hmm...
var key = ""
for(var i in key){ console.log(i) }
contains
//undefined
Object.getOwnPropertyNames(key)
//["length"]
Object.getOwnPropertySymbols(key)
//[]
window[key]
//undefined
At first I thought this was one of those JS behaviors and was ready to submit it to JSWTF but the behavior runs properly in another chrome tab:
for(var i in ""){ console.log('ran',i) }
//undefined
How did a value get assigned to a blank string?
Where is it?
What is the for loop doing?
edit: The same page in firefox returns expected behavior in console. I have not tested other browsers
You have an ES6 shim on the original page which adds the function contains() to the String prototype. You can do this yourself by doing something like:
String.prototype.contains =
function(e) {
return this.indexOf(e) > -1;
};
The ES6 function ultimately standardized on is includes(), so you'll probably see that function name change in the future when a developer updates the shim.
As javascript developers we all have to write a lot of for loops. Before a couple of months I saw an alternative syntax, which I really liked. However, I'm now interested, is there any other nice way.
Let's say that I have an array of data representing users in a system. What I did before is:
var users = [
{ name: "A"},
{ name: "B"},
{ name: "C"},
{ name: "D"},
{ name: "E"}
];
var numOfUsers = users.length;
for(var i=0; i<numOfUsers; i++) {
var user = users[i];
// ...
}
There is one additional row var user = users[i];. Normally I feel more comfortable if I have user instead of users[i]. So, the new way:
for(var i=0; user=users[i]; i++) {
// ...
}
I'm also wondering if the second approach produces problems in some of the browsers. One of my colleagues reported that this syntax is a little bit buggy under IE.
Edit:
Thankfully, the answers below pointed me out to the right direction. If some of the elements of the array is falsy then the loop will stop. There is some kind of solution:
for(var i=0; typeof (user=users[i]) !== "undefined"; i++) {
// ...
}
But that's too much for me. So, I guess that I'll use this syntax only when I'm 100% sure that all the elements are truly (which means never :)).
In your “new” approach, you don’t need numOfUsers any more.
As for the potential problems: This approach relies on all users[i] having values evaluating to true for the loop to continue (and user becoming undefined, equal to false and therefor ending the loop after the last user is processed) – but sometimes you might have data where not every record evaluates to true, but “false-y” values might also occur in the data – and in that case, this approach of course fails.
The problem with this approach:
for(var i=0; user=users[i]; i++) {
// ...
}
...is that it assumes user won't be "falsey" (0, "", null, undefined, NaN, or of course false) until you've gone past the end of the array. So it'll work well with an array of non-null object references, but if you then get in the habit of using it, it will bite you when you have an array of numbers, or strings, or such.
The other reason not to declare variables within the for construct is that it's misleading: Those variables are not scoped to the for loop, they're function-wide. (JavaScript's var doesn't have block scope, only function or global scope; ES6 will get let which will have block scope.)
On modern JavaScript engines (or with an "ES5 shim"), you can of course do this:
users.forEach(function(user) {
// ...
});
...which has the advantage of brevity and not having to declare i or numUsers or even user (since it's an argument to the iteration callback, and nicely scoped to that). If you're worried about the runtime cost of doing a function call for each entry, don't be. It'll be washed out by whatever actual work you're doing in the function.
I'm amazed if the second syntax works at all your middle operation should evaluate to true for each loop you want to complete and false as soon as you want to be done looping. As for any issues with your first for loop, a JavaScript is function scoped so that inner var statement will still leak to the containing function (as well as that i). This is different than most other languages that have block scoping. It's not so much of a problem but something to keep in mind if you are debugging.
If you are already using jQuery, you can use the jQuery.each function to loop over your arrays.
In any case you can look at the source code of that function and copy the relevant parts for your own foreach function: http://james.padolsey.com/jquery/#v=1.10.2&fn=jQuery.each
I have been looking through some of the jQuery source and I ran across the merge function. Here is the source code for it:
function merge(first, second) {
var l = second.length,
i = first.length,
j = 0;
if (typeof l === "number") {
for (; j < l; j++) {
first[i++] = second[j];
}
} else {
while (second[j] !== undefined) {
first[i++] = second[j++];
}
}
first.length = i;
return first;
}
While I understand the code, something doesn't make sense to me. Particularly the if (typeof l === "number") part. I have tried passing an array to this function when I have manually changed the .length property to something like "3" and checked it's type and I still get a type of number.
My question is when would the length property ever not be a type of number in JavaScript arrays?
The merge function you're looking at is a public jQuery method: jQuery.merge() or $.merge(). So you might think that reading the jQuery.merge() documentation would shed some light on this question.
Unfortunately, it doesn't, at least as of this writing.
As explained in the other answers and comments, this test for a numeric length property will always succeed when second is an Array, because an Array will always have a numeric length. The only possible reason to have this test in the code is to handle the case where second is not an Array.
But the documentation doesn't mention this case at all. It only discusses cases where both first and second are both native JavaScript Array objects, not "array-like" objects.
If this documented use was all this function did, it would be pretty useless, since JavaScript provides a perfectly good native array.concat(array) method that can be used instead of jQuery.merge().
So it would seem that the real purpose of this function must be in its undocumented use. Let's try it out in the Chrome console on the jQuery.merge() doc page. Open the developer tools, select the Console tab, and enter an example similar to the ones in the doc:
jQuery.merge( [ 'a', 'b' ], [ 'c', 'd' ] )
and then try the same thing with .concat():
[ 'a', 'b' ].concat([ 'c', 'd' ])
They will both log exactly the same thing:
["a", "b", "c", "d"]
Since this is jQuery, the most likely case of an "array-like" object would be a jQuery object. For example, that doc page currently has three <h3> elements on it, so we can get them with:
$('h3')
That will log:
[►<h3>…</h3>, ►<h3>…</h3>, ►<h3>…</h3>]
That isn't an array, but it looks a lot like one, and it does have a numeric length property which we can check with:
typeof $('h3').length
So let's try it with concat:
['x'].concat( $('h3') )
Oops. That should give us an array of four elements, 'x' followed by the three DOM elements. Instead, i gives us an array of two element, with the jQuery object as the second element:
[►e.fn.init[3] ]
It looks like jQuery's "array-like" object isn't array-like enough for .concat().
Oddly enough, some other array methods do with work jQuery's array-like object, such as .slice():
Array.prototype.slice.call( $('h3'), 1 )
That logs the correct result, an array of two elements:
[►<h3>…</h3>, ►<h3>…</h3>]
So let's try $.merge():
$.merge( ['x'], $('h3') )
Sure enough, that logs what we expected:
["x", ►<h3>…</h3>, ►<h3>…</h3>, ►<h3>…</h3>]
There's also another way to do this. jQuery provides a .toArray() method, so we can do the same thing by combining that with .concat():
['x'].concat( $('h3').toArray() )
That logs the same thing as the $.merge() call.
What if first is a jQuery object?
$.merge( $('h1'), $('h3') )
That works too:
[<h1 class="entry-title">jQuery.merge()</h1>,
►<h3>…</h3>, ►<h3>…</h3>, ►<h3>…</h3>]
So it would appear that this is the purpose of this method: to do array.concat()-like operations on jQuery objects.
But wait! This still doesn't answer the question of why that extra code is in there for the non-numeric .length. After all, a jQuery object does have a numeric .length, so we still haven't exercised the code for the non-numeric case.
So this part of the code must be there for some purpose other than handling jQuery objects. What could that be?
We can find out a bit more by going to the jQuery source repository and locating the file in the src directory that has the merge: code (it happens to be core.js). Then use the Blame button and search within the page for the merge: code to see what the commit comment for the code is. Again, the code we're wondering about now is the else clause, where typeof l is not a number.
The commit was by John Resig on 2009-12-09, with a comment "Rewrote merge() (faster and less obtuse now). Fixed #5610." There's a bit of controversy in the discussion on that page:
Well, your version is faster only in the special (very rare) case of overwritted length property. In all other cases, my one was little faster.
Also you are not considering the case obj.length = new Number(N) - but yeah it is not so relevant I suppose.
This helps explain it a little, but what is issue #5610? Maybe that will tell us. Don't see an issue tracker on the GitHub repo? jQuery has its own issue tracker and if we search there for #5610 (or do a Google search for jquery issue 5610, we finally find issue #5610:
MAKEARRAY CAN CAUSE BROWSER CRASH ON NON ARRAY OBJECTS WITH LENGTH PROPERTY
Description
I haven't tested this with 1.4
For an object with a length property that casts to a non-zero positive integer, makeArray will create an array with that length filled with undefined.
>>> jQuery.makeArray({'length': '5'})
[undefined, undefined, undefined, undefined, undefined]
If the length property is zero, negative or a decimal then Firefox and Safari both hang. (FF shows the unresponsive script error, Safari hangs until it crashes)
>>> jQuery.makeArray({'length': '0'})
>>> jQuery.makeArray({'length': '5.2'})
>>> jQuery.makeArray({'length': '-3'})
You could be passing an object to the function, e.g.
merge ({ length: 'mylength' }, { length: 'mylength' });
if second is undefined or null or an object not supporting length, l will not be a number. I cannot say why second might be undefined or null as it's being passed from the caller.
I'm currently working with some data using Javascript that is in the form of an array. The array may contain an empty entry at the end, such as [1,2,]. In Google Chrome and Firefox, the length of that example would be 2; however, in IE, the length is 3.
In short: Internet Explorer is giving a different length for an array in Javascript than Google Chrome and Firefox. Is there a way to standardize this behavior across all browsers?
Code:
var a = [1,];
alert(a.length);
EDIT:
A lot of answers are saying not to have a trailing comma, however, the data is given to me in this way.
NEVER have trailing commas in IE. Period.
That goes for ARRAYs too
Javascript Browser Quirks - array.Length
To handle your edit this works (tested in in IE8):
if (a[a.length-1]==null) a.length--; // or a.pop()
For a safer test, please look at the other suggestion on this page: Length of Array Differs In Internet Explorer With Trailing Comma - DEMO HERE
By the way, never heard the words elision or elided before - learn something new here every day
No. IE incorrectly interprets a single trailing comma as an elision and adds one to the length when it shouldn't (ECMA-262 sect. 11.1.4).
Edit
To clear up the confusion here, IE treats a single trailing comma in an array literal (incorrectly) as an elision, which means it increments array's length property but does not create a property. In other words, given:
var a = [0,1,];
In IE, a.length is 3, but there is no property a[2]. So if an appropriate solution is to remove only elided members from the end of an array (which is likely the best solution if they are an issue), then:
function tidyTrailingElisions(array) {
var i = array.length;
while (!array.hasOwnProperty(--i)) {}
array.length = ++i;
return array;
}
will remove only elided members from the end of the array (that is, properties that don't exist), it will not remove them elsewhere, nor will it waste time iterating over the entire array (which can result in elided members being added as undefined). To add to Array.prototype:
Array.prototype.tidyTrailingElisions = function() {
var i = this.length;
while ( !this.hasOwnProperty(--i)) {}
this.length = ++i;
return this;
};
Note that this is how Array.prorotype.filter works, it doesn't iterate over elided members (it uses a hasOwnProperty test and removes any elided mebers as part of filtering the array).
Try removing empty elements:
a = a.filter(function(){return true});
Then it should work the same way in IE and other browsers.
Update: if the JS version is lower than 1.6, try this:
Array.prototype.clean = function() {
for (var i = 0; i < this.length; i++) {
if (this[i] == undefined) {
this.splice(i, 1);
i--;
}
}
return this;
};
a.clean()
Is there a way to get (from somewhere) the number of elements in a Javascript object?? (i.e. constant-time complexity).
I can't find a property or method that retrieves that information. So far I can only think of doing an iteration through the whole collection, but that's linear time.
It's strange there is no direct access to the size of the object, don't you think.
EDIT:
I'm talking about the Object object (not objects in general):
var obj = new Object ;
Although JS implementations might keep track of such a value internally, there's no standard way to get it.
In the past, Mozilla's Javascript variant exposed the non-standard __count__, but it has been removed with version 1.8.5.
For cross-browser scripting you're stuck with explicitly iterating over the properties and checking hasOwnProperty():
function countProperties(obj) {
var count = 0;
for(var prop in obj) {
if(obj.hasOwnProperty(prop))
++count;
}
return count;
}
In case of ECMAScript 5 capable implementations, this can also be written as (Kudos to Avi Flax)
function countProperties(obj) {
return Object.keys(obj).length;
}
Keep in mind that you'll also miss properties which aren't enumerable (eg an array's length).
If you're using a framework like jQuery, Prototype, Mootools, $whatever-the-newest-hype, check if they come with their own collections API, which might be a better solution to your problem than using native JS objects.
To do this in any ES5-compatible environment
Object.keys(obj).length
(Browser support from here)
(Doc on Object.keys here, includes method you can add to non-ECMA5 browsers)
if you are already using jQuery in your build just do this:
$(yourObject).length
It works nicely for me on objects, and I already had jQuery as a dependancy.
function count(){
var c= 0;
for(var p in this) if(this.hasOwnProperty(p))++c;
return c;
}
var O={a: 1, b: 2, c: 3};
count.call(O);
AFAIK, there is no way to do this reliably, unless you switch to an array. Which honestly, doesn't seem strange - it's seems pretty straight forward to me that arrays are countable, and objects aren't.
Probably the closest you'll get is something like this
// Monkey patching on purpose to make a point
Object.prototype.length = function()
{
var i = 0;
for ( var p in this ) i++;
return i;
}
alert( {foo:"bar", bar: "baz"}.length() ); // alerts 3
But this creates problems, or at least questions. All user-created properties are counted, including the _length function itself! And while in this simple example you could avoid it by just using a normal function, that doesn't mean you can stop other scripts from doing this. so what do you do? Ignore function properties?
Object.prototype.length = function()
{
var i = 0;
for ( var p in this )
{
if ( 'function' == typeof this[p] ) continue;
i++;
}
return i;
}
alert( {foo:"bar", bar: "baz"}.length() ); // alerts 2
In the end, I think you should probably ditch the idea of making your objects countable and figure out another way to do whatever it is you're doing.
The concept of number/length/dimensionality doesn't really make sense for an Object, and needing it suggests you really want an Array to me.
Edit: Pointed out to me that you want an O(1) for this. To the best of my knowledge no such way exists I'm afraid.
With jquery :
$(parent)[0].childElementCount