chemerisuk / better-dom

Live extension playground
http://chemerisuk.github.io/better-dom/
MIT License
545 stars 35 forks source link

Method $Node#data is deprecated in 1.7.3 #12

Closed chemerisuk closed 10 years ago

chemerisuk commented 10 years ago

Writing a code for the DOM often requires to have an extra data associated with an element instance. Therefore better-dom has $Node.prototype.data method which provides access to a private key/value map to associate any kind of object with an $Element.

var body = DOM.find("body");

body.data("key1", {a: 1, b: 2});
body.data("key2", 1985);
...
body.data("key1"); // => {a: 1, b: 2}
body.data("key2"); // => 1985
...
body.data("key1", null);
body.data("key1"); // => null

The problem

This method has a useful ability to grab and parse data from data-* attributes that have name equal to key in the private key-value map object. For example if we have an element like below:

<div id="foo" data-bar="welcome" data-obj='{"x":1;"y":"2"}'></div>

The private key-value map of the element will be automatically populated when you call the data method with appropriate keys at the first time:

var foo = DOM.find("#foo");

foo.data("bar"); // => string "welcome"
foo.data("obj"); // => object {"x":1;"y":"2"}

On the one hand this feature is very useful, because saves lines of code. On the other hand it introduces a naming collision with the get and set methods:

foo.get("data-bar"); // => string "welcome"
foo.get("data-obj"); // => string '{"x":1;"y":"2"}'

Having both foo.data("bar") and foo.get("data-bar") to do similar thing looks confusing.

Conflict with the HTMLElement.dataset

The data method behaviour looks similar to what the HTML5 dataset attribute does. But the dataset attribute has several important differences:

  1. it supports only string values
  2. it updates data-* attribute value when appropriate property was changed (that makes it very slow comparing to regular JavaScript objects BTW).

So data and dataset have very different behaviour despite on similar names. This introduces a small logical conflict with existing standards.

The sense and goals of the $Node.prototype.data method is very close to storing private data associated with an $Element instance. Thats why I've made the phrase private key-value map to be bold several times.

Solution

In JavaScript private properties are usually prefixed with the underscore symbol. Why not to use this syntax we are all familiar with that also explains the sense of the method clearer than the data word:

foo.get("_bar"); // => string "welcome"
foo.get("_obj"); // => object {"x":1;"y":"2"}

It looks very clear for me and takes the same number of symbols and a line with data word. So in theory you should write programs with the same speed :)

Another advantage of this change is that it allows to reduce amount of code, because the data method has similar code parts with getter and setter. Also, the new watch method can reuse some logic to listen to changes of the private key/value map as well.

The change for getter and setter submitted for the 1.7.3, because it won't break anything. $Node.prototype.data will be removed in 1.8, the next minor release.

mrzafod commented 10 years ago

Hi, Maksim. Your suggestion seems right. But it is useful to have all private properties in a single object in case you are watching for changes in a whole $Element._data

chemerisuk commented 10 years ago

Hey Roman. All data will be stored in the private _data object as it was before. But I'm curious why do you need to monitor the whole object? Accessing private properties is unsafe and a bad practice in general. Can you use something like below (didn't look at the latest APIs for Object.observe but you'll get the idea):

var changableObject = {};

el.data("changable", changableObject);

...

Object.observe(el.data("changable"), function(changes) { 
  // handle the object changes
});
chemerisuk commented 10 years ago

The final syntax that I decided to use in better-dom 2 is like below.

Having

<a id="link" data-test-param="value"></a>

This is how it was in better-dom 1.7.3+:

DOM.find("#link").get("_test-param"); // => "value"

This is how it will be in better-dom 2+:

DOM.find("#link").get("_testParam"); // => "value"

Notice, that I use camel case for the argument key. There are several reasons to use came case:

Reason 1. We treat camel case variables as a JavaScript stuff This is a habit. There are lots of code style guides that encourage to use came case for naming variables. So any such name looks like a stuff related to JavaScript. And it is a good thing, because the main propose of private properties is to be used in code.

Reason 2. We usually use dash-separated names in HTML Variables in camel case is clearly different from dash-separated. It allows to have a better separation between:

DOM.find("#link").get("data-test-param"); // => "value"

that should be treated like a "reading an attribute value", and

DOM.find("#link").get("_testParam"); // => "value"

that actually "reads a private property".

Now despite on returning the same value they are more different from each other.