There have been a lot of times I have needed some information for a bililiteRange
plugin that was associated with the underlying element, rather than a specific range. For instance, in bililiteRange(element).text('foo')
then bililiteRange(element).undo()
the undo needs to know about the previous text change. jQuery has a data()
method that attaches an object to the element and you can add fields to that object. Actually, it only attaches an index that points to the actual object, since at least in some browsers the garbage collector had trouble with Javascript objects attached to DOM elements and you ended up with memory leaks. I'm not sure if that is still a problem, but it's an easy enough pattern to implement so I used it.
I didn't want to be jQuery-dependent and I wanted to be able to some more sophisticated things with my data, so I implemented my own. At its simplest, just use:
var data = bililiteRange(element).data();
data.foo = 'bar';
assert(bililiteRange(element).data().foo == 'bar');
bililiteRange(element).data()
returns an object that you can add fields to and they will be saved across multiple calls to bililiteRange
.
The sophisticated part is for when I want to add information for every bililiteRange. Then I have to add the field to the prototype of the data object. That uses a "global" bililiteRange method, bililiteRange.data()
to define the field:
bililiteRange.data(name [,descriptor])
Defines bililiteRange(element).data()[name]
for all bililiteRanges by adding name
to the data prototype. Without using the descriptor
parameter that's pretty useless, but descriptor
lets you add some useful parameters. Options include:
value
- Set the default value for the field
enumerable
- Sets the
enumerable
property for the field. Iffalse
, thenfor (key in bililiteRange(element).data())
will not include that property, andJSON.stringify(bililiteRange(element).data())
will not list it. Defaults totrue
. monitored
- Makes the field "monitored", meaning that whenever it is set, an event is triggered:
bililiteRange(element).dispatch({ type: 'bililiteRangeData', bubbles: true, detail: {name: name, value: value} })
The following fields are reserved (in addition to anything in Object.prototype
: values
is an object that actually stores the values (since internally I use set/get
), sourceRange
is the range from which the data object was originally defined, toJSON
is the method for making JSON.stringify
work, and all
returns an object that represents all enumerable values, not just ones set for this data object.
The data object has a custom toJSON
method, so JSON.stringify(bililiteRange(element).data())
produces the expected results, even though the fields are not simple values. This will only display the fields that have been explicitly set, not the ones that are defined on the prototype. To get all the enumerable fields, use bililiteRange(element).data().all
(either in for (...in)
or JSON.stringify
).
Internet Explorer 8 does not allow setting custom properties on plain Javascript elements, so the enumerable
and monitored
fields are ignored, and all
is always undefined
.
Examples
The bililiteRange utilities redefine text()
to allow "autoindenting" with a third parameter. We can implement this in an editable element using the data
to hold the option:
bililiteRange.data ('autoindent', {value: false}); // default
var rng = bililiteRange(editor);
document.querySelector('#autoindentCheckbox').addEventListener('change', function() { rng.data().autoindent = this.checked });
rng.listen('keydown', function(evt){
if (evt.keyCode == 13){
rng.bounds('selection').text('\n','end', rng.data().autoindent).select();
evt.preventDefault();
}
});
And fancyText
does exactly that. See the demo.
tab-size
is inconsistently implemented, but we could use a data property to monitor for changes and set the style based on that:
bililiteRange.data ('tabSize', {monitored: true});
var rng = bililiteRange(editor);
rng.listen('bililiteRangeData', function(evt){
if (evt.detail && evt.detail.name == 'tabSize') {
editor.style.tabSize =
editor.style.mozTabSize = evt.detail.value;
}
});
rng.data().tabSize = 4;
Leave a Reply