How does jQuery 2.x uniquely identify an element (in .data()) - javascript

In jQuery 1.x elements would receive a unique identifier (a cache key) when required, stored in the ele[jQuery.expando] property of a node, set by this line of code. Mootools uses similar logic.
So in jQuery 1.x
var test = document.createElement("span");
console.log(test[jQuery.expando]);//undefined
$(test).data("test", {stuff:2});
console.log(test[jQuery.expando]);//some uid
I was curious, as I'm in the process of implementing similar logic, how jQuery identify a node in 2.x. Referencing a line number in the github repo would be excellent!

jQuery 2 still uses this method, the property is simply harder to detect because it uses a hidden random number as part of the property name, not just the jQuery.expando
Its lines 42 and 43 that add the unique data property to the element, the Data.js file is in effect a (mostly) stand-alone class definition that jQuery uses to handle the data storage functionality. I admit that the code is rather convoluted in the repo, but using that as a way of learning how jQuery works is not really recommended, use the commented source at code.jquery.com/jquery-2.0.3.js to do that instead, it makes understanding how everything interacts much easier.
See https://github.com/jquery/jquery/blob/6318ae6ab90d4b450dfadf32ab95fe52ed6331cb/src/data/Data.js#L32-51

Related

JavaScript - Executing code when property modified / Data binding

I'm trying to build a simple binding framework in JavaScript. I want to have a model with getters and setters that keeps everything consistent, i.e. updating one property can affect others and then JavaScript that then binds that to fields in the page.
The problem I'm having is I need to add some extra code to the setters on the model when I bind so that updating a property executes code that's already in the setter to keep the model consistent and then update the bound input(s).
I thought about renaming the existing property and adding a new property with the original name which could set the original property and update the input but I can't see a way to rename the field.
Is there any way to either rename a property with a getter/setter, modify a setter or get the code in a setter so I can copy that?
Alternatively, is there any other way to achieve what I'm trying to do?
I'm trying to keep separation between business logic (the model) and the UI (the html) without requiring the person writing the model to have to think about the UI / binding at all.
I also don't want to use any of the big libraries like Angular or Knockout as they're a lot of code to include in the page for pretty limited requirements, plus this is a project we've been developing / maintaining for 20 years+ so we don't want to be using a library that has a history of massive breaking changes (Angular).
We currently need to support IE10/11 and modern versions of Chrome / Edge / Firefox / Safari (iOS and Mac). However, if there's something that doesn't support all of these, we're open to doing a "nice" way with a "nasty" fallback until we can drop IE support.
If it makes any difference, we are using TypeScript to write the JavaScript.
Edit:
Since submitting my question, I've found this. It says you can rename a property with:
Object.defineProperty(o, new_key, Object.getOwnPropertyDescriptor(o, old_key));
delete o[old_key];
This is working for renaming simple variable type properties but not for renaming properties with getters and setters. I'm not sure why, although my properties with getters / setters return false when I do hasOwnProperty on them.
Edit2:
Turns out that TypeScript was adding the properties against the prototype instead of the object and this is why they weren't accessible. I called Object.getOwnPropertyDescriptor(o.__proto__, old_key) instead and this gave me the descriptor I needed.
I've found that you can edit / extend the setter by doing the following:
var propDescriptor = Object.getOwnPropertyDescriptor(model, key);
propDescriptor.set = function (value) {
setter.call(model, value);
//Do extra stuff here
}
Object.defineProperty(model, key, propDescriptor);
This means that the property still maintains consistency while allowing you to extend it to update the bound input field.

Writing to a Firebase from within a Polymer element

I am working on an assignment for a course in "Coding the Humanities" which involves writing a custom web component. This means I am required to use Polymer even though as far as I can see there is absolutely no added value to doing so.
I want to create a literal chat "room" in which users input a character to identify themselves and can walk around the room bumping into one another after the fashion of robotfindskitten.
My idea was to write each character and its position to a Firebase location, updating everyone's positions in real time, so I need the Firebase JS client- using core-ajax for REST requests isn't fast enough.
The GitHub readme for the core-firebase element consists of a link to a less than helpful component page.
Looking at the core-firebase element itself, I don't see anything that corresponds to the 'value' event; locationChanged has a 'child-added' event handler, but that's it.
Am I crazy for thinking the core-firebase element is just very incomplete? Should I try to write my own 'value' handler? If so, do I just add it to the locationChanged property of the object passed to Polymer()? I'm very confused - I know enough JS that what's happening in the core-firebase code is straddling the limits of my comprehension. (Which might have to do with the this keyword, I don't know.) Any input here would be appreciated. (And yes, I've already remarked to the instructor that I could have handled this using plain old jQuery and Firebase if I didn't have to use Polymer. No word as yet on that.)
Looking at the commits for core-firebase it looks like it's had about two days work on it plus some maintenance, so it wouldn't be surprising if there are missing features.
One nice part about Polymer is that it interops very well with other ways of writing apps. It's totally reasonable and supported to use jQuery and Firebase directly to read from firebase and react to changes. You can still make good use of polymer's templating and databinding by doing this within an element of your own and using Polymer's data binding, templating, and plain old DOM events to propagate those changes throughout your app and render them onto the page.

What is App.hiddenDivs in this jQuery code sample?

I was learning some performance related JQuery tips here
Can you tell the meaning of App.hiddenDivs ?
Here App is a javascript object. You can create a javascript object like:
var App = new Object();
and set the App Object member like:
App.hiddenDivs = $('div.hidden');
Then you can access the object in you application like:
App.hiddenDivs.find('span');
It's like Caching jQuery Objects but at the Application level.
App will be an object, and hiddenDivs will be a property on it;
By setting $('div.hidden') to it, it allows you to re-use the result (the jQuery object containing all div.hidden elements), rather than querying the DOM for it each time. This will result in a micro-speed-improvement.
In general, App.hiddenDivs has absolutely nothing to do with jQuery. In this code, it just happens to be the place where a jQuery collection gets stored.
App is a JavaScript object of some sort (we don't know what it really is given the context, and it doesn't matter--it's just some imaginary object which is part of some imaginary code in which this example could live). It could have been something as simple as:
var App = {};
hiddenDivs is a property of that object which is defined to hold the return value of the jQuery code, $('div.hidden').
That return value is a jQuery collection containing all DIVs in the DOM with the class of hidden. Further operations on such DIVs can then be run against that property, rather than re-seeking them in the DOM.
The page you link to, while probably holding some valuable advice, is poorly written. The author should provide a bit more background info on what he's writing about, and should have explained the expected level of knowledge his readers should have. Furthermore, he should offer code examples which build on each other as the explanations progress in order to provide some continuity for the more basic readers to follow.

Obtaining a reference to the streamManager of the internal object model on twitter.com

For a Greasemonkey script running on twitter.com, I need to access the twttr.streams.TweetStream instance of the main timeline (dubbed 'Home' internally) programmatically. I'm using Firebug and Javascript Deminifier to bring their JS code into a readable form. That way, I could previously work out that I could access it via twttr.app.currentPage().streamManager.streams.current in my GM script.
This has worked perfectly over the last months. Today, Twitter seems to have changed their code, breaking my approach (not their fault, obviously ;)).
I can still get to the current page via twttr.app.currentPage(). However, it doesn't have a streamManager field anymore.
I've tried various paths to get there, but all were dead ends. Unfortunately, I don't completely understand the class system they are using yet. It seems like the streamManager property is still there -- on a mixin called mixins/streamablePage, which should be provided by the class twttr.components.pages.Home. I can't figure out how to access it, though. (Or if the class system hides it in some impenetrable way.) That mixin also provides a getStreamManager() method, but I can't access that either, e.g. via twttr.app.currentPage().getStreamManager(). Is there any trick I need to perform to get access to these mixins from the outside?
Can anyone spot an alternative method to get to this instance? Note that I need the original instance used on the timeline page. Yes, I could easily create a new instance via new twttr.streams.TweetStream(), but I'm trying to hook into the original events.
I am perfectly aware that this use case is as unsupported as it gets, that's why I'm asking you, not them. :) For the record, I'm not attempting to do anything evil, just providing additional functionality for myself.
Until they change it again, twttr.app.currentPage()._instance.getStreamManager()

Prototype or jQuery for DOM manipulation (client-side dynamic content)

I need to know which of these two JavaScript frameworks is better for client-side dynamic content modification for known DOM elements (by id), in terms of performance, memory usage, etc.:
Prototype's $('id').update(content)
jQuery's jQuery('#id').html(content)
EDIT: My real concerns are clarified at the end of the question.
BTW, both libraries coexist with no conflict in my app, because I'm using RichFaces for JSF development, that's why I can use "jQuery" instead of "$".
I have at least 20 updatable areas in my page, and for each one I prepare content (tables, option lists, etc.), based on some user-defined client-side criteria filtering or some AJAX event, etc., like this:
var html = [];
int idx = 0;
...
html[idx++] = '<tr><td class="cell"><span class="link" title="View" onclick="myFunction(';
html[idx++] = param;
html[idx++] = ')"></span>';
html[idx++] = someText;
html[idx++] = '</td></tr>';
...
So here comes the question, which is better to use:
// Prototype's
$('myId').update(html.join(''));
// or jQuery's
jQuery('#myId').html(html.join(''));
Other needed functions are hide() and show(), which are present in both frameworks. Which is better? Also I'm needing to enable/disable form controls, and to read/set their values.
Note that I know my updatable area's id (I don't need CSS selectors at this point). And I must tell that I'm saving these queried objects in some data structure for later use, so they are requested just once when the page is rendered, like this:
MyData = {div1:jQuery('#id1'), div2:$('id2'), ...};
...
div1.update('content 1');
div2.html('content 2');
So, which is the best practice?
EDIT: Clarifying, I'm mostly concerned about:
Memory usage by these saved objects (it seems to me that jQuery objects add too much overhead), while OTOH my DOM elements are already modified by Prototype's extensions (loaded by default by Richfaces).
Performance (time) and memory leakage (garbage collection for replaced elements?) when updating the DOM. From the source code, I could see that Prototype replaces the innerHTML and does something with inline scripts. jQuery seems to free memory when calling "empty()" before replacing content.
Please correct me if needed...
You're better off going with jQuery. Both frameworks are great (and I use them both), but in your case jQuery is probably the winner.
In my opinion prototype provides a more natural syntax for javascript development. It does this at the cost of adding methods to some of the core classes, but it's also motivated by ruby where this is the norm.
jQuery on the other hand is far superior at dom manipulation and event observation. Your code will be more concise and manageable in these cases and will benefit from great performance features like event delegation. jQuery also has a much larger community and way more plugins and code samples.
If you're only interested in the three basic methods "update", "hide" and "show" then jQuery is better suited. It is aimed more at DOM manipulation which is exactly what you need. Then again you could do each of those things in a couple of lines of code, saving the 26KB needed to transfer the jQuery library.
Since your worry is in memory usage look at the jQuery file, it is 77KB uncompressed, how much work do you suppose that is for the browser to execute? Probably much more than that freed by calling empty() on a typical DIV.
And you mention Prototype is already in use on the site in which case you shouldn't be adding another library. jQuery's abilities are a subset of Prototype's.
This question is nearly a year old so you've probably made your decision by now. For anyone else reading the answer is simple; If something's not broken don't fix it, you already have one capable library installed so use that.
I would go for jQuery. Prototype used to modify default JS objects, which is fine but it means you have to be careful. I believe this is no longer the case though. JQuery also has a large plugin repository and the jquery UI extension for widgets. Btw. With JQuery you can use the familiar dollar sign as well.

Categories