This question already has answers here:
Do DOM tree elements with IDs become global properties?
(5 answers)
Closed 9 years ago.
If I write html like this:
<div id="foo">Foo<div>
window.foo returns a dom-element and window.document.getElementById("foo") === window.foo returns true.
Why is that? And why does everyone use getElementById?
And on a sidenote: Why was overriding window.foo forbidden in IE7/8? And what happens if I set window.foo = "bar"?
I am not sure about the historical perspective, but HTML 5 specifies that elements are candidates to be directly exposed as properties on the window object if they have an id attribute:
The Window interface supports named properties. The supported property
names at any moment consist of the following, in tree order, ignoring
later duplicates:
[...]
the value of the id content attribute of any HTML element in the active document with a non-empty id content attribute.
The problem with this definition is that it only guarantees that if there is a <div id="foo">Foo<div> then window.foo will be defined. It does not guarantee what exactly its value will be (read the spec for the rules on how that is determined; for example, it might return a collection).
So it turns out the answer to "why use getElementById ever?" is simple: because you can depend on it to return what you expect without needing to take into account the whole document.
In general placing something inside the window object will make it global. For example:
var A = function() {
window.test = "bla";
console.log(test);
}
var B = function() {
console.log(test);
}
A();
B();
However that's not a good practice. You should not rely on any global object, because you may want to move your code to a browser that doesn't have window. Or to nodejs for example.
I find window.foo a little bit wrong, because you may have code that creates a global variable called foo. So, using getElementById ensures you that you always get DOM element.
Window.foo is working fine in your scenario, but what if the Id is something like this "foo-test" instead of "foo", you can see it will not work. it is because Javascript variables are not allowed for dashes in it....
Whereas it will work fine in case of document.getElementById
Related
It may not be common knowledge, but "Javascript on many (all?) modern browsers seems to create variables on the window object for DOM elements with IDs".
Knowing this I'd like to be able to delete these variables and below is some code I've tried without success. Also consider my screenshot of console.log statements, which first indicates why as not being a property of window (it should come in between "webkitUrl" and "window"), but nevertheless in the two console.log statements that immediately follow the first, window/why is shown as the div from the document?
Why can't these automatically generated variables be deleted from their parent object, just like any other?
<!DOCTYPE html>
<html>
<head>
<script>
setTimeout(function() { //poor man's document/ready
var allElements = document.getElementsByTagName("*"), elementId;
for (var i=allElements.length; i--; ) {
elementId = allElements[i].id;
if (elementId && window[elementId] instanceof HTMLElement) {
delete window.why;
console.log(window);
console.log(window.why);
console.log(why);
}
}
});
</script>
</head>
<body>
<div id="why"></div>
</body>
</html>
That's because these properties are not directly stored in window. Instead, it behaves like a proxy.
For example, see what Firefox does when you use getOwnPropertyDescriptor on WindowProperties (from which window inherits):
bool WindowNamedPropertiesHandler::getOwnPropDescriptor(
JSContext* aCx, JS::Handle<JSObject*> aProxy, JS::Handle<jsid> aId,
bool /* unused */, JS::MutableHandle<JS::PropertyDescriptor> aDesc
) const {
// ...
Element* element = document->GetElementById(str);
if (element) {
JS::Rooted<JS::Value> v(aCx);
if (!WrapObject(aCx, element, &v)) {
return false;
}
FillPropertyDescriptor(aDesc, aProxy, 0, v);
return true;
}
// ...
}
You might think that when you set an ID to some element, Firefox stores it as a global property. But it doesn't work like this: it's when you attempt to access the property that Firefox will use GetElementById to know if there is some element with that ID or not, and answer accordingly.
Even more, deletions are explicitly forbidden:
bool WindowNamedPropertiesHandler::delete_(
JSContext* aCx, JS::Handle<JSObject*> aProxy,
JS::Handle<jsid> aId, JS::ObjectOpResult &aResult
) const {
return aResult.failCantDeleteWindowNamedProperty();
}
This behavior is hard-coded and you can't prevent it. So if these properties annoy you, just override them by declaring your own variables.
I'd like to be able to delete these variables
There's absolutely no reason to do that. These globals are there for backwards compatibility and browsers hardly will remove them. However, they were created in a way so that you can simply ignore them - they won't interfere with any of your variables.
Just declare var why; in your script and the reference to the element is gone.
Why can't these automatically generated variables be deleted from their parent object?
First of all, variables cannot be deleted in general. Only object properties can - it's just that some global variables are deleteable properties of the global object.
The element references probably can't be deleted because a) they're not real properties but rather proxied b) they're not properties of the global object itself, but of its prototype or c) they're properties of an extra variable record in the global scope. Just consider them to be magic and don't care any more.
This question already has answers here:
What is the difference between these prototype declaration? [duplicate]
(3 answers)
Closed 8 years ago.
I'm trying to decide how to set up my functions in the prototype for my main library.
Should I use:
Library.prototype.funcA = function () {.....};
Library.prototype.fucnB = function () {.....};
etc..
or
Library.prototype = {
funcA: function () {.....},
funcB: function () {.....},
etc..
};
So basically the first choice adds all my functions to the prototype. The second option replaces the prototype with an object containing all my functions. Does it matter?
I would go with the first option.
You don't want to completely replace the prototype, as you never know what has been added from another project.
If it's something completely self-contained that only you are working on, the second is an ok option. But it is still not a good habit to get into so you don't inadvertently blow away some functionality something else is counting on.
In this case, no, it doesn't matter. It's your object, and you're not attempting to inherit from anything, so overwriting the prototype (as opposed to appending to it) doesn't matter.
In the general case, yes, it might matter a lot. You're clobbering whatever existing prototypal methods were available to the object. You shouldn't do that unless you're very sure that your code owns the object in question. Conversely, appending methods to the prototype requires thought as well; other objects may share a prototype with the object whose prototype you're modifying.
It does matter. You have to note that the prototype is an object. So your statement "The second option replaces the prototype with an object containing all my functions." is false. It just reset the prototype object.
So using :
Library.prototype = {
funcA: function () {.....},
funcB: function () {.....},
etc..
};
Is faster, but you delete every prototype function you had before that assignment while :
Library.prototype.funcA
is adding a properties.
So, if you have to add a property (not erase one) use :
Library.prototype.funcA
Else, assign an object.
If Library has prototypes properties/methods that you don't want to lose you would want to go with adding them via augmenting the already present prototype. Otherwise it's really up to personal preference. I like the second method because it looks cleaner, but I have used both in my code.
This question already has answers here:
Using the variable "name" doesn't work with a JS object
(4 answers)
Closed 7 years ago.
Lets say we have this code segment :
var name = ["Apples","Oranges","Strawberries"];
console.log(name.length);
This code produces this weird result of 27 !! The issue seems to be with using the variable name as 'name' which seems like a reserved keyword.
But can anyone explain why this weird behavior ?
It refers to window.name, which is the name of the window.
You can use the window's name to target hyperlinks, but it's not typically useful.
More info on window.name: https://developer.mozilla.org/en-US/docs/Web/API/Window.name
Just testing in chrome:
You can't stop var name from being window.name, which is a string. No matter what you set the value to, it will be cast as a string so that it is a valid window name. So, name.length is the amount of characters in the string. It's best to avoid variable or be very careful about them!
As I can see from some of the other comments, this is an odd concept if you're new to it. The concern is over what window.name refers to. window.name is the name of the window. Any use of it is naming the window.
Defending that Chrome's behavior is logical:
If this var document = 'foo' did what it looks like it would do, you would have overwritten window.document - the document object - with a string. That would be quite the problem. name is a property of window just like document and has a use that shouldn't be (and can't be in Chrome) replaced.
In the global scope, when you do var name = ["Apples","Oranges","Strawberries"];, it's the same as window.name = ["Apples","Oranges","Strawberries"];.
window.name must be a string, so it assign ["Apples","Oranges","Strawberries"].toString() to it which is "Apples,Oranges,Strawberries".
What is the scope of variables in JavaScript? is a good start. Basically, when you're declaring name outside of a function, you're actually hiding the window.name value, which is a very, very bad idea (be very careful about any global variables you declare - they are actually values of the window object - including hiding existing values.
As others have said, the fact that Google Chrome forces the type to string is probably a Chrome quirk (although somewhat understandable), but the underlying cause is that you're simply doing something "dangerous" you didn't realize you're doing :)
the window object has a property "name";
if you define 'name' in a closure function you may get 3
you defined var name in the global object, so you just initialized a string obj;
(function(){
var name = ["Apples","Oranges","Strawberries"];
consol.log(name.length);
})()
var name = ["Apples","Oranges","Strawberries"];
the global window.name is a build in string object(reserved), so name.toString() is called when you excuete the line above;
As pointed out by others, in the when you are using the variable named name in the global scope it is treated as window.name.
From experimentation under Google Chrome, window.name gets the value as a string, like so: "Apples,Oranges,Strawberries", This behaviour is because Google Chrome is forcing window.name to be string. This explains why name.length will report 27 as that is the length of the string given.
This behaviour is not present in IE, Opera or Firefox which assing the array ["Apples","Oranges","Strawberries"] and report 3 on `name.length as expected, therefore I guess this is a quirk of Google Chrome.
I've noticed that when I am trying to check for a variable within the window object, javascript will return the DOM node of an id matching the variable name. Consider the following code:
<html>
<body>
<div id='hello'>
</div>
<script>console.log(window.hello);</script>
</body>
</html>
In this example, the console will output the DOM object representation of <div id='hello'> and any of its descendants. I've also observed that overwriting window.hello doesn't affect the DOM and subsequent operations show the new assignment.
Is there a way to type check specifically for a DOM object? Alternatively, is there a way to avoid this behavior? Although not difficult, having to be conscious of page ID's when choosing namespaces seems like it could become very counter intuitive.
Some browsers will create a global variable for each id value in the document. In general, you should use:
document.getElementById("hello")
to retrieve DOM elements by id.
This is yet another reason why you should generally avoid using the global namespace any more than required as it is significantly polluted by this browser practice.
If you really want to test whether something is DOM object, you can see how that is done here: JavaScript isDOM -- How do you check if a JavaScript Object is a DOM Object?, but you should, in general, not need to do this for regular DOM programming purposes.
This is actually the HTML5 standard:
http://www.whatwg.org/specs/web-apps/current-work/#named-access-on-the-window-object
A way to be safe about this is to make all your javascript objects and functions within a "namespace":
var objs = {
someObject: document.getElementById( "someThing" ),
someFunction: function() {},
hello: {
...
}
};
//Now you can access it without interfering with the DOM "hello"
console.log( window.objs.hello );
This question already has answers here:
Are there legitimate uses for JavaScript's "with" statement?
(33 answers)
Closed 9 years ago.
Pretty much every resource documenting with that I can find warns against using it, mostly because if a variable is not defined it may have unpredictable effects.
I want to understand it so that I can make effective use of it - after all, it's there for a reason. Even eval has its non-evil uses!
So, with that in mind, let's say I want to delete all child nodes from an element, without using elem.innerHTML = "";
Would the following be safe?
with(elem) while(firstChild) removeChild(firstChild);
Note that at this point I'm not caring for readability, just functionality. Since firstChild is a property of, and removeChild a method of, all element nodes, it should be fine to use with this way, right?
Similarly, let's say I want to set some styles.
with(elem.style) {
color = "red";
backgroundColor = "black";
fontWeight = "bold";
}
Since all of these are properties of the style object (even if not defined in the stylesheet, they are there as empty strings), it's fine to use with like this, right?
Am I missing something, or is the constant warning to not use with similar to the one against PHP's mysql extension: a protection against dumb programmers?
The with keyword in Javascript is a bit of a throwback to when it was competing with VBScript in the late 90s. It's still there, but disabled if you 'use strict'; and considered an error by just about every Javascript validator out there.
There are two major problems with it, both related to the way scope works in Javascript:
var color = "green";
var foo = "bar";
with(elem.style) {
color = "red";
foo = "something else";
}
What's color now? What is foo? Not only is this confusing code, but due to how Javascript looks up variables in the with scope it's also very slow code as now every statement has an additional scope to search for each variable used.
This is one of the reasons why modern frameworks like jQuery and Prototype use chaining. They use call and apply on functions to control what this represents, meaning that you make calls to this.something rather than with(this){something}.
So to answer your question: there is one good use of with in Javascript - allowing IE4 and IE5 websites written in the 90s to still work today.
That's it. You shouldn't use it in new code.
The only safe use of with is not using it at all. There's no task that can't be accomplished without it and modern standards outright disable it in strict mode.
For all purposes it is considered a design mistake that is only preserved for backwards compatibility.
with simply puts the variable on top of the stack of "maps" searched for variables.
Normal stack is (searched top to bottom)
window
(root - just having window)
So if you have
var foo = { document: "doc.pdf" };
window.myFunc = function(){
with( foo ){
alert( document );
}
}
The stack within with is
foo
window
(root - just having window)
It will surely print foo.document and not window.document.
In this case it's obvious that you shouldn't use document like that, as it's typically used in browsers. But ECMAScript specification doesn't define that, so in other environments, other variables may be on the stack by default (even more of them).
The danger is that whatever you call within the with statement has that on the stack too.
This would fail on the document.url call:
// Some 3rd-party library
function redirect( url ){
document.url = url; // url is undefined in document
}
var bar = { document: "20x20" };
with( bar ){
redirect(); //
}
The with statement is just a shorthand for writing recurring accesses to objects:
For
foobar.foo.bar.baz = 'bubu';
foobar.foo.bar.buz = 'baba';
you can write
with(foobar.foo.bar){
baz = 'bubu';
buz = 'baba';
}
Thats nice! BUT will foobar.foo.bar be modified or will the global variables baz and buz modified?
In some cases it is impossible to know for sure.
JavaScript provides a better alternative
var better = foobar.foo.bar;
better.baz = 'bubu';
better.buz = 'baba';
Now there is no ambiguity.