Does the jQuery.clone method have side effects? - javascript

If $dom is a jQuery element,
Can this line be removed safely if a is not used after?
var a = $dom.clone(true,true);
As the method clone the dom, I think $dom.clone(false,false) do not have side effects. If think that method cannot thrown an error too. I'm more sceptical about the clone of events. I think if the cloned element is not reattached, it doesn't have effects, but I'm not sure. Is there any event that can be propagated to detached dom event? (if yes, then I'm not sure we can removed safely that line)
https://api.jquery.com/clone/
The aim of the question is to determine if the absence of #nosideeffects is a bug in the externs definition of .clone() here : https://github.com/google/closure-compiler/blob/master/contrib/externs/jquery-1.9.js#L405

Whether the absense of that annotation is a bug in terms of the Closure Compiler's definition of "no side effects" is pretty much up to Google. :-)
In at least jQuery 1.x, calling .clone with true, true mutates jQuery's state information, information it doesn't just hold on the elements returned and associated with the jQuery object that ends up referenced by a: jQuery's data cache for the element (which is used both for "data" and for event handler information) is not stored on the element itself, it's stored in a structure within jQuery that's keyed by an expando property on the element. So it's not a truly pure function; it mutates the state of something: jQuery's data cache.
In jQuery 2.x and above, I believe the information is stored on the element itself (since 2.x and above don't support IE6 and IE7, which couldn't do GC across the DOM/JS boundary correctly).

Related

Why can't a certain DOM element have CSS styles

While developing an Angular app I've come across the following issue: an SVG element didn't have styles from its class (even though it was defined in <styles> tag) and what's more peculiar (and the point of the question): Chrome and Firefox DevTools didn't allow adding element styles manually.
Note the missing element.styles block in the right pane.
However, if I edit container element HTML and just copy and paste back the markup - it suddenly appears and everything works as expected. So this must happen due to the way the element is added to DOM programmatically. And since this behaviour is identical in both Chrome and Firefox, it is most likely a feature, rather than a bug.
So how can this be achieved purposedly?
P.S. For those interested in Angular part, here is a GitHub issue that I reported for this case (also contains a reproduction repo): https://github.com/angular/material2/issues/15727
So I've found the answer myself. TL;DR: element was created with document.createElementNS with an unknown namespace (or empty), which made it an Element, rather than SVGElement. Styles are not defined for base Elements according to the specification.
So obviously the key was to check the type of the element - and both Chrome and Firefox have shown that that SVG is created as a base Element:
The next question was how did it happen. TL;DR for this section: due to a bug in Angular's new experimental compiler (probably) it was added with an invalid namespace and the rest is according to specification.
Search through Angular's source revealed the way it creates elements: createElementNS.
Quick experiment with that function has proven the initial guess that the problem was with the element's interface (Element), and it can be reproduced by passing an invalid namespace.
And DOM specification revealed exactly why does that happen:
createElementNS steps:
The internal createElementNS steps...:
...
4. Return the result of creating an element given document, localName, namespace, prefix, is, and with the synchronous custom elements flag set.
Creating an element is defined as follows:
To create an element, given a document, localName, namespace, and optional prefix, is, and synchronous custom elements flag, run these steps:
...
4. Let definition be the result of looking up a custom element definition given document, namespace, localName, and is.
Looked up definition turns out to be NULL in this case:
To look up a custom element definition, given a document, namespace, localName, and is, perform the following steps. They will return either a custom element definition or null:
1. If namespace is not the HTML namespace, return null.
Returning to createElementNS steps from before:
...
7. Otherwise: [definition is null]
Let interface be the element interface for localName and namespace.
Set result to a new element that implements interface, with no attributes, namespace set to namespace, namespace prefix set to prefix, local name set to localName, custom element state set to "uncustomized", custom element definition set to null, is value set to is, and node document set to document.
...
And finally, the element interface is defined as follows:
The element interface for any name and namespace is Element, unless stated otherwise.
As for inability to add custom styles, that is due to the missing style property on the base Element. That's probably due to the specification of the style attribute:
All HTML elements may have the style content attribute set. This is a style attribute as defined by the CSS Style Attributes specification.
HTML elements are:
To ease migration from HTML to XML, UAs conforming to this specification will place elements in HTML in the http://www.w3.org/1999/xhtml namespace, at least for the purposes of the DOM and CSS. The term "HTML elements" refers to any element in that namespace, even in XML documents.
However, style property exists in SVGElement, and probably some other base prototypes, so there must be some other specifications that allow that. But I thought that's out of scope of my original question.
And that concludes my research.

Why $('div#my') is returning Object Reference instead of HTML Tag? [duplicate]

This question already has an answer here:
Why sometimes jQuery selector returns something like "a.fn.init"? [closed]
(1 answer)
Closed 6 years ago.
I don't know what happen to my Chrome browser, but all of sudden the behavior of doing $('div#my') in console is totally different from before. One time I've experienced this but later it somehow recovered, so I don't know how to reproduce it, and today it happened again.
Please watch the video:http://peaceevertvimg.org/jq.php.
In the video I do $('div#my') in two different browsers:
the first browser is not chrome but I believe it imitates Chrome so its behavior is what I expect and what I have almost always been experienced. Because currently my chrome is not working as expected so I have to use it to demonstrate my expection: when you do $('div#my)` you see directly the html TAG, and you can easily see the tag's html content, which is "something" in this case.
In contrast, in my chrome browser, the result is different, when I do $('div#my') I see an Object(n.fn.init), and I can't see the "something" immediately, which of course is very inconvenient. But before, I am pretty sure it was not like this, the behavior WAS exactly like that in the first browser.
The simple webpage in this video is http://peaceevertvimg.org/jquery.php, you can go test for yourself in chrome browser. And I am pretty sure most of you will see the first behavior. What happened to my chrome?(I've disabled all expansions and updated it to the latest version)
By the way, is "HTML Tag" and "Object Reference" the right words to describe these two different outcome?
*************Update*************
If the video is not sufficient to understand what I ask and what I want to fix, these two pictures may help.
Picture#1: This is the "normal" behavior I've expected:
Picture#2: This is the current behavior I am experiencing:
You can see the big differences, the first one is much more intuitive, revealing key information immediately, while the 2nd one is not, at least to me. What causes this problem and how do I go back to the first one?
$('div#my') doesn't return a DOM reference. It returns a jQuery wrapper around the found elements.
$('div#my')[0] would return a DOM reference. Or, forget jQuery and use:
document.getElementById("my");
...and you will get a DOM reference directly
Also, since there should/will only ever be one element with a given ID, it is unnecessary to use div#my, just use #my.
Assuming we have a <div id=someDiv>, and then we write:
console.log($("#someDiv"));
console.log($("#someDiv")[0]);
Chrome shows this:
In the first log, we see that the result is a jQuery object that contains one element (the div). In the second, we see the element directly.
Now, depending on what version of Chrome you have, you may see the first one reported simply as [Object object], but that doesn't change the underlying result.
From: Devx (http://www.devx.com/codemag/Article/40923)
Selectors let you select DOM elements so that you can apply
functionality to them with jQuery's operational methods. jQuery uses a
CSS 3.0 syntax (plus some extensions) to select single or multiple
elements in a document. You're probably already familiar with the CSS
syntax from HTML styling. Even if you're not, it's fairly easy to pick
up the key CSS selector features. I'll go as far as saying that jQuery
is the reason I really started to grok CSS. Using CSS syntax you can
select elements by ID, CSS class, attribute filters, or by their
relationship to other elements. You can even chain filter conditions
together. Look at this simple example, which selects all second-column
TD elements in a table using a simple selector: $("#gdEntries
td:nth-child(2)").
The jQuery Object: The Wrapped Set: Selectors return a jQuery object
known as the "wrapped set," which is an array-like structure that
contains all the selected DOM elements. You can iterate over the
wrapped set like an array or access individual elements via the
indexer ($(sel)[0] for example). More importantly, you can also apply
jQuery functions against all the selected elements. - See more at:
http://www.devx.com/codemag/Article/40923#sthash.l8Mo8CbH.dpuf
What you are seeing is said jQuery object returned by jQuery.fn.init().
What is going on is that jQuery() is being defined as jQuery.fn.init() which is another way to say jQuery.prototype.init() which is the selector function! What this means is that no one would call jQuery.fn.init() or jQuery.init() because jQuery() IS .init().
Some more info and a look at the jQuery code here: Help understanding jQuery's jQuery.fn.init Why is init in fn
As for a solution to your problem: https://chrome.google.com/webstore/detail/jquery-console-fix/jlmkkpkcgomkdpfhgjlpaaonhafnjgob?hl=en

How jQuery data() breaks circular reference

I have read an why it's better and how it's implemented. But what i don't really understand is how does it break the circular reference?.
how does it break the reference circle?
$(div1).data('item', div2);
$(div2).data('item', div1);
like example, the divs above point to each other, how is it prevented? I have a hunch, but i just want to make sure if my hunch is right.
The circular reference problem happens in some browsers when you put a reference to a DOM object on a DOM object as a property on that DOM object. Then, you have two DOM objects pointing at each other. Removing a DOM object with a custom property on it doesn't clear that custom property. A garbage collector that isn't that smart doesn't realize that this DOM reference doesn't count so it gets stuck and there are several ways that this can lead to leaks.
.data() solves this problem because the .data() data is NOT on the DOM object. It's just a javascript data structure that can be associated with the DOM object via a unique string ID.
The one confusing part of this is that when you read with .data("key") and the key isn't found in the javascript .data() data structure, then and only then, jQuery will look for an attribute on the DOM object called "data-key". But whenever you write with .data("key", "myData"), it never writes to the DOM object, only to the javascript data structure.
Thus, since .data() never writes the data to the DOM object, there can't be any of these types of circular references that some browsers have trouble with.
There are some other useful things to know about the .data() data structure. When you use jQuery's .remove() to remove elements from the DOM or when you call $(elem).html("new html"), jQuery clears the .data() data on any removed items. This is one case where it's good not to mix jQuery with plain javascript. If you're using .data(), then you should always remove items from the DOM using jQuery functions so .data() is cleaned up appropriately. Otherwise, you can get memory leaks this way (both the .data() data can leak and any removed DOM objects that are referenced in the .data() can leak. But, if you only use jQuery methods for removing items from the DOM (including the replacing of innerHTML), then jQuery will clean things up appropriately and there will be no leaks.
So, for example, this will create a memory leak:
// suppose elem is a DOM element reference
// store some data in jQuery's data storage on behalf of a DOM element
$(elem).data("someKey", "someValue");
// remove DOM element with plain Javascript
elem.parentNode.removeChild(elem);
Because you removed the DOM element with plain Javascript, jQuery did not have a chance to clean up the data you previously stored. The DOM element itself will be garbage collected, but the .data() value you previously stored is now orphaned in jQuery's storage and is essentially a "leak" as it will likely never be cleared. On the other hand, if you do this:
$(elem).data("someKey", "someValue");
$(elem).remove();
Then, jQuery will see that you're removing the DOM element and will also clear the data you stored with .data().
A fairly simple way to see how it works is to create a couple line script with a non-minimized version of jQuery and then just step through a call to $(elem).data("key", "whatever") in the debugger and watch how it works.

jQuery ID Selector ("#id") Returns Array

I'm using jQuery v1.6.1 in noConflict mode.
I'm using id selectors such as $j("#divID").value to get values of stored items.
Unfortunately, $j("#inputID") is returning a list of items, so I have to use the $j("divID")[0].value to get the value of the object. The [0] seems unnecessary, since there is, by definition, only one html element with any given id.
Is this the appropriate way to gets values from an IDed object? Or is there a better way?
Thanks!
$j("#divID").val() will work just fine.
Per the jQuery documentation, .val() will return the value of the first element in the set of matched elements.
It's worthwhile understanding conceptually how jQuery works in order to see why it works this way. The result of any selector query is a jQuery object. It's that jQuery object that contains the myriad of methods that jQuery offers. .val() is one of those methods as are things like .fadeIn(), .hide(), etc... Those methods are not methods on a DOM object, but methods of a jQuery object. Because jQuery objects are general purpose and can hold 0, 1 or more DOM objects in their internal array, you get the same jQuery object back from a jQuery selector call whether the results have 0, 1 or more DOM objects in it.
Thus $j("#divID") that contains only one object returns the same type of object as $j(".rows") which might contain hundreds of DOM objects. This vastly simplifies jQuery programming because you don't have to do things differently depending upon how many objects come back from the selector query.
When you refer to $j("divID")[0], you are reaching into the jQuery object's internal array of DOM objects (that was populated on the selector query) and fetching the first DOM object in that array. At that point, you have a normal DOM object, not a jQuery object and you can use normal DOM methods or attributes on it. Occasionally this is required (to fetch the actual DOM object), but usually, it's easier to just use the methods that jQuery provides on the jQuery object. There are lots of advantages to using them such as you can chain multiple requests to most methods and it will iterate over all the DOM objects in it's internal array for you automatically.
For example, you you called this: $j("rows-even").hide() and there were 20 rows with that class, then all of them would each be operated on by the hide() method with no more code than this. Of you could chain multiple methods together like this: $j("rows-even").slideUp().slideDown(). In this case, you're running an animation and jQuery will chain these two animations together, automatically starting the second one when the first one finishes. It's all pretty useful in many circumstances and can save a ton of code over what would normally have to be written using plain JS.
$j("#divID") returns a jQuery object. In order to get the value of the selected element you have to call its val method to get the value.
Use $j("#divID").val();

__defineSetter__ on innerHTML stops it from rendering

i'm trying to create a watch method for HTML elements, using __define[GS]etter__ when a property is changed. It reacts just fine when i set the value, but if the property listened to, is innerHTML, it somehow fails to render the given string. So basically, when im adding something to innerHTML it doesn't show.
Im using the watch method described in this previous question:
Watch for object properties changes in JavaScript
I could of cause just not listen to innerHTML changes, but i'm also wondering if the __defineSetter__ somehow prevents original handling of setting the value.
Thanks!
That shim code doesn't actually write-through: when you set a property, the value is only remembered on the watch-wrapper and not passed down to the underlying object. It's designed for pure JavaScript objects whose properties have no side-effects (like changing the DOM, in innerHTML's case).
Making it write-through would be a pain since there's no way to directly call the prototype's setter. You'd have to temporarily remove the property, write to the underlying property, then put it back in place.
However it's not really worth pursuing IMO. DOM Nodes are permitted to be ‘host objects’, so there is no guarantee any of the native-JavaScript object-property functions will work on them at all.
(In any case, adding new members onto the Object prototype is generally considered a Really Bad Idea.)
I could of cause just not listen to innerHTML changes
I think that's best, yes.
Ok update.
I found this page on MSDN, which has exactly what i need:
http://msdn.microsoft.com/en-us/library/dd229916(VS.85).aspx
But the Object.getOwnPropertyDescriptor way of doing things apparently only works in IE. Bummer. Any ideas would be appreciated.
I found another webpage with something that looked interesting, but i couldn't quite get it to do what i wanted:
http://www.refactory.org/s/recent/tag/accessors
I've decided to find a workaround for now, something with a timer that checks the attribute for changes and attribute change evnets for browsers that support that.
But if any one finds a solution for the question plz post :)

Categories

Resources