Is it possible to augment a DOM element by adding a new property to it as augmenting a normal JavaScript object? For example, can we add a new property of a button or an input, say a value that indicates when was the last time user click on it?
Yes. You can sometimes find it described as bad form. The obvious drawback is that in later versions of Web standards, the DOM element may have new properties added to it which will clash with your custom property, so if you do use this technique, choose a name that is unlikely to clash, e.g. incorporate your company name into it.
Another potential problem is that DOM objects in some browsers are not properly garbage collected. Instead they are reference counted. This means that if you create a system of circular references through custom properties, you may cause a memory leak.
For example, this may cause a memory leak, because there is a circular reference between the DOM and javascript objects:
var myJsObj = {};
var myDomElt = document.getElementById('myId');
myJsObj.domElt = myDomElt;
myDomElt.jsObj = myJsObj;
It is, however, safe to add a property that just holds a primitive value:
myDomElt.fullTitle = "My DOM Element";
Although the warning that the DOM standard may change is still important; what if HTML7 defines a fullTitle property on DOM elements? Your site will have unpredictable behaviour.
An alternative is to use the $.data feature of jQuery:
$(myDomElt).data('fullTitle', 'My Dom Element');
var retrieved = $(myDomElt).data('fullTitle');
It's possible but is a really bad idea. Daniel Earwicker's answer mentions some reasons why it's a bad idea. I'd add these:
DOM nodes are host objects and host objects can do what they like. Specifically, there is no requirement in the ECMAScript spec for host objects to allow this kind of extension, so browsers are not obliged to allow it. In particular, new browsers may choose not to, and existing code relying on it will break.
Not all host objects in existing browsers allow it. For example, text nodes and all ActiveX objects (such as XMLHttpRequest in older IE and XMLDOM objects, used for parsing XML) in IE do not, and failure behaviour varies from throwing errors to silent failure.
In IE, the ability to add properties can be switched off for a whole document, including all nodes within the document, with the line document.expando = false;. Thus if any of the code in your page includes this line, all code relying on adding properties to DOM nodes will fail.
Related
JavaScript lets you add arbitrary properties and methods to any object, including DOM nodes. Assuming the property was well namespaced (something like _myLib_propertyName) so that it would be unlikely to create a conflict, are there any good reasons not to stash data in DOM nodes?
Are there any good use cases for doing so?
I imagine doing this frequently could contribute to a sloppy coding style or code that is confusing or counter-intuitive, but it seems like there would also be times when tucking "custom" properties into DOM nodes would be an effective and expedient technique.
No, it is usually a bad idea to store your own properties on DOM nodes.
DOM nodes are host objects and host objects can do what they like. Specifically, there is no requirement in the ECMAScript spec for host objects to allow this kind of extension, so browsers are not obliged to allow it. In particular, new browsers may choose not to, and existing code relying on it will break.
Not all host objects in existing browsers allow it. For example, text nodes and all ActiveX objects (such as XMLHttpRequest and XMLDOM objects, used for parsing XML) in IE do not, and failure behaviour varies from throwing errors to silent failure.
In IE, the ability to add properties can be switched off for a whole document, including all nodes within the document, with the line document.expando = false;. Thus if any of the code in your page includes this line, all code relying on adding properties to DOM nodes will fail.
I think more than anything, the best reason not to store data in the DOM is because the DOM is meant to represent content structure and styling for an HTML page. While you could surely add data to the nodes in a well-namespaced manner as to avoid conflicts, you're introducing data state into the visual representation.
I'm trying to find an example for or against storing data in the DOM, and scratching my head to find a convincing case. In the past, I've found that the separation of the data state from the visual state has saved headache during re-designs of sites I've worked on.
If you look at HTML 5 there is the data attribute for fields that will allow you to store information for the field.
Don't be surprised to run into trouble with IE 6 & 7. They are very inconsistent with setAttribute vs set as a property.
And if you're not careful you could set circular references and give yourself a memory leak.
http://www.ibm.com/developerworks/web/library/wa-memleak/
I always set node properties as a last resort, and when I do I'm extra careful with my code and test more heavily than usual.
For what it's worth, the D3.js library makes extensive use of custom properties on DOM nodes. In particular for the following packages that introduce interaction behaviors:
https://github.com/d3/d3-zoom - the __zoom property
https://github.com/d3/d3-brush - the __brush property
D3 also provides a mechanism for putting your own custom properties on DOM elements called d3.local. The point of that utility is to generate property names that are guaranteed not to conflict with the DOM API.
The primary risk I can see associated with custom properties on DOM nodes is accidentally picking a name that already has some meaning in the DOM API. If you use d3.local or maybe prefix everything with __, that should avoid conflicts.
I have a question about the following piece of Javascript code, it is very basic.
var elNote = document.getElementById('note');
elNote.TextContent = 'Hello';
I am new to Javascript and would like to know what is going on here. My book will most likely explain it later, but I would like to learn now.
It is my understanding that elNote gets assigned the html element named note.
My question is if html elements have built in properties, since we select the Property TextConent from the element note. It is also my understanding that elements such as note, are NODES. Does Javascript create and "inject" properties into elements so that they can be modified. I don't know, I'm really trying to think what is going on. thanks.
Yes you can use for example:
elNote.textContent = 'Hello world'; // For raw text content
elNote.innerHTML = '<h1>Hello</h1> world'; // For raw text content
elNote.value = 123; // Tipically for <input> and <button> tags value
Javascript is a loosely typed language, it assigns properties and related methods to the nodes when DOM is defined and its properties vary browser to browser and tier versions.
as far as TextContent is concerned it is described here
These properties are specific to the type of nodes like
document it self is also a node but it is a top level node and it does not contains properties like innerText etc.
For more deep understanding of Document Object Model please refer this wiki
Summing up:
The W3C DOM and WHATWG DOM standards form the basis of the DOM implemented in most modern browsers. Many browsers offer extensions beyond the standard, so care must be exercised when using them on the web where documents may be accessed by various browsers with different DOMs. Copied from this source It looks like DOM is defined by Browsers in the form of javascript readable / accessible objects and their properties vary browser to browser.
I'm building a function in which I would find it useful to determine if a string is intended to specify an event type. (e.g., click, keypress, blur..) Currently, I'm comparing it to an array of names I grabbed from the standard, but I don't like this solution for a few reasons:
It's ugly. A hard-coded array of 167 items seems awfully inelegant.
Different browser environments likely support different events. I could use different (or additional) lists per-browser, but that just exacerbates the prior issue, and would be godawful to maintain.
There are, I believe, situations where events can be dynamically created. I can't possibly detect those beforehand.
My search-fu seems to have failed me on this point, so I turn to my clever and generous fellow users of Stack Overflow. My question, in short, is this:
How can I get a list, at runtime, of currently recognized event types?
Object.keys(HTMLElement.prototype).filter(function(k) { return /^on/.test(k); });
Similar to SLaks' answer, but you could run it on the window object instead to get an exhaustive list of events.
Object.keys(window).filter(function(k) { return /^on/.test(k); });
You don't say why you want to do this, so answers can only address the specific question of "how to tell if a string is an 'on' event".
I don't think there's a definitive strategy for that. Using a list of all possible event types collated from implementations, standards or specifications might be reasonably reliable but will not be perfect since javascript implementations are free to introduce any new type of event they wish. So the list would need to be updated frequently and you'd have to at least test and possibly update the list on every new browser and version that is released. Also, all events may not be supported by all browsers.
Using strategies like collecting the enumerable "on" properties of a DOM object or it's prototype also don't suit since hosts aren't required to implement inheritance on DOM objects and some don't make them enumerable (such as Safari v9 at least).
Another solution is to see if 'on' + string is a standard property of a DOM object, so:
function isPossibleEventName(s) {
var div = document.createElement('div');
return ('on' + s) in div;
}
['click','input','blur','foo','bar','paste'].forEach(function(s){
document.write('<br>Possible event ' + s + ': ' + isPossibleEventName(s));
});
That will at least tell you if the host supports a particular event type, however it may need to be tested on different element types since not all elements necessarily support all events. Also, it doesn't tell you if the string might be an "on" event in some other host (but neither will collating "on" properties of an element's prototype).
While this should work in all browsers, there is some doubt if the above will work in older Firefox. However, this post says otherwise and it seems OK in version 43. You should test versions back as far as you think reasonable.
Here's my go-to reference, pretty comprehensive.
I've been writing Javascript for years, but just now realized I don't entirely understand how these "HTML-y" methods actually interact with the DOM.
My assumption is that when HTML is interpreted into the DOM, the browser keeps references as to which HTML element became which DOM node.
So for .getElementById when you select an element, the browser returns the relevant node by reference. Why not just do .getNodeById then and be more correct?
My assumption is that .innerHTML is interpreted by the browser into the DOM at runtime. However, in older browsers like IE7, .innerHTML cannot be used to create table nodes. This suggests to me that it was originally intended just to modify the text properties of existing nodes, but then it seems strange that it exists at all, and we don't just use .innerText.
I guess I'm just confused by some Javascript history.
Well, the thing is, things like .innerHTML and .getElementById aren't really JavaScript objects at all. They're what the language specification calls "Host Objects" - it's a form of FFI. What happens when you call .getElementById in most browsers is that the string is marshalled from a JS string to a DOM string and then passed to C++ code.
In fact, they're allowed to have pretty much whatever semantics they please - the language makes no guarantees about how they act (this is also true for other DOM objects like timers and XHR).
well if you really want to know about the internals and want to digg into the deep rabbit hole of rendering engines i'd give you a start by pointing you to certain source files of chromium:
http://src.chromium.org/viewvc/blink/trunk/Source/core/dom/Element.cpp
you could find stuff like innerHTML and how it actually works in it. (around line 2425)
.getElementById() will just resolve to a document element that matches the specified id and will return a reference to it.
its some kind of identifier with prototyped methods, getters and setters to interact with that element. as soon as you interact with methods like innerHTML etc. you will simply tell the engine to execute that action on the element that is referenced inside the element reference.
just digg around inside the source code. you might get a good insight:
http://src.chromium.org/viewvc/blink/trunk/Source/core/dom/
I've been looking for a straight answer for this (I can think of lots of possiblities, but I'd like to know the true reason):
jQuery provides a .data() method for associating data with DOM Element objects. What makes this necessary? Is there a problem adding properties (or methods) directly to DOM Element Objects? What is it?
Is there a problem adding properties (or methods) directly to DOM Element Objects?
Potentially.
There is no web standard that says you can add arbitrary properties to DOM nodes. They are ‘host objects’ with browser-specific implementations, not ‘native JavaScript objects’ which according to ECMA-262 you can do what you like with. Other host objects will not allow you to add arbitrary properties.
In reality since the earliest browsers did allow you to do it, it's a de facto standard that you can anyway... unless you deliberately tell IE to disallow it by setting document.expando= false. You probably wouldn't do that yourself, but if you're writing a script to be deployed elsewhere it might concern you.
There is a practical problem with arbitrary-properties in that you don't really know that the arbitrary name you have chosen doesn't have an existing meaning in some browser you haven't tested yet, or in a future version of a browser or standard that doesn't exist yet. Add a property element.sausage= true, and you can't be sure that no browser anywhere in space and time will use that as a signal to engage the exciting DOM Sausage Make The Browser Crash feature. So if you do add an arbitrary property, make sure to give it an unlikely name, for example element._mylibraryname_sausage= true. This also helps prevent namespace conflicts with other script components that might add arbitrary properties.
There is a further problem in IE in that properties you add are incorrectly treated as attributes. If you serialise the element with innerHTML you'll get an unexpected attribute in the output, eg. <p _mylibraryname_sausage="true">. Should you then assign that HTML string to another element, you'll get a property in the new element, potentially confusing your script.
(Note this only happens for properties whose values are simple types; Objects, Arrays and Functions do not show up in serialised HTML. I wish jQuery knew about this, because the way it works around it to implement the data method is absolutely terrible, results in bugs, and slows down many simple DOM operations.)
I think you can add all the properties you want, as long as you only have to use them yourself and the property is not a method or some object containing methods. What's wrong with that is that methods can create memory leaks in browsers. Especially when you use closures in such methods, the browser may not be able to complete garbage cleaning which causing scattered peaces of memory to stay occupied.
This link explains it nicely.
here you'll find a description of several common memory leak patterns
It has to do with the fact that DOM in IE is not managed by JScript, which makes it completely different environment to access. This leads to the memory leaks http://www.crockford.com/javascript/memory/leak.html. Another reason is that, when people use innerHTML to copy nodes, all those added properties are not transfered.