-
-
Save brettz9/4093766 to your computer and use it in GitHub Desktop.
/** | |
* Add dataset support to elements | |
* No globals, no overriding prototype with non-standard methods, | |
* handles CamelCase properly, attempts to use standard | |
* Object.defineProperty() (and Function bind()) methods, | |
* falls back to native implementation when existing | |
* Inspired by http://code.eligrey.com/html5/dataset/ | |
* (via https://github.com/adalgiso/html5-dataset/blob/master/html5-dataset.js ) | |
* Depends on Function.bind and Object.defineProperty/Object.getOwnPropertyDescriptor (polyfills below) | |
* All code below is Licensed under the X11/MIT License | |
*/ | |
// Inspired by https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Function/bind#Compatibility | |
if (!Function.prototype.bind) { | |
Function.prototype.bind = function (oThis) { | |
'use strict'; | |
if (typeof this !== "function") { | |
// closest thing possible to the ECMAScript 5 internal IsCallable function | |
throw new TypeError("Function.prototype.bind - what is trying to be bound is not callable"); | |
} | |
var aArgs = Array.prototype.slice.call(arguments, 1), | |
fToBind = this, | |
FNOP = function () {}, | |
fBound = function () { | |
return fToBind.apply( | |
this instanceof FNOP && oThis ? this : oThis, | |
aArgs.concat(Array.prototype.slice.call(arguments)) | |
); | |
}; | |
FNOP.prototype = this.prototype; | |
fBound.prototype = new FNOP(); | |
return fBound; | |
}; | |
} | |
/* | |
* Xccessors Standard: Cross-browser ECMAScript 5 accessors | |
* http://purl.eligrey.com/github/Xccessors | |
* | |
* 2010-06-21 | |
* | |
* By Eli Grey, http://eligrey.com | |
* | |
* A shim that partially implements Object.defineProperty, | |
* Object.getOwnPropertyDescriptor, and Object.defineProperties in browsers that have | |
* legacy __(define|lookup)[GS]etter__ support. | |
* | |
* Licensed under the X11/MIT License | |
* See LICENSE.md | |
*/ | |
// Removed a few JSLint options as Notepad++ JSLint validator complaining and | |
// made comply with JSLint; also moved 'use strict' inside function | |
/*jslint white: true, undef: true, plusplus: true, | |
bitwise: true, regexp: true, newcap: true, maxlen: 90 */ | |
/*! @source http://purl.eligrey.com/github/Xccessors/blob/master/xccessors-standard.js*/ | |
(function () { | |
'use strict'; | |
var ObjectProto = Object.prototype, | |
defineGetter = ObjectProto.__defineGetter__, | |
defineSetter = ObjectProto.__defineSetter__, | |
lookupGetter = ObjectProto.__lookupGetter__, | |
lookupSetter = ObjectProto.__lookupSetter__, | |
hasOwnProp = ObjectProto.hasOwnProperty; | |
if (defineGetter && defineSetter && lookupGetter && lookupSetter) { | |
if (!Object.defineProperty) { | |
Object.defineProperty = function (obj, prop, descriptor) { | |
if (arguments.length < 3) { // all arguments required | |
throw new TypeError("Arguments not optional"); | |
} | |
prop += ""; // convert prop to string | |
if (hasOwnProp.call(descriptor, "value")) { | |
if (!lookupGetter.call(obj, prop) && !lookupSetter.call(obj, prop)) { | |
// data property defined and no pre-existing accessors | |
obj[prop] = descriptor.value; | |
} | |
if ((hasOwnProp.call(descriptor, "get") || | |
hasOwnProp.call(descriptor, "set"))) | |
{ | |
// descriptor has a value prop but accessor already exists | |
throw new TypeError("Cannot specify an accessor and a value"); | |
} | |
} | |
// can't switch off these features in ECMAScript 3 | |
// so throw a TypeError if any are false | |
if (!(descriptor.writable && descriptor.enumerable && | |
descriptor.configurable)) | |
{ | |
throw new TypeError( | |
"This implementation of Object.defineProperty does not support" + | |
" false for configurable, enumerable, or writable." | |
); | |
} | |
if (descriptor.get) { | |
defineGetter.call(obj, prop, descriptor.get); | |
} | |
if (descriptor.set) { | |
defineSetter.call(obj, prop, descriptor.set); | |
} | |
return obj; | |
}; | |
} | |
if (!Object.getOwnPropertyDescriptor) { | |
Object.getOwnPropertyDescriptor = function (obj, prop) { | |
if (arguments.length < 2) { // all arguments required | |
throw new TypeError("Arguments not optional."); | |
} | |
prop += ""; // convert prop to string | |
var descriptor = { | |
configurable: true, | |
enumerable : true, | |
writable : true | |
}, | |
getter = lookupGetter.call(obj, prop), | |
setter = lookupSetter.call(obj, prop); | |
if (!hasOwnProp.call(obj, prop)) { | |
// property doesn't exist or is inherited | |
return descriptor; | |
} | |
if (!getter && !setter) { // not an accessor so return prop | |
descriptor.value = obj[prop]; | |
return descriptor; | |
} | |
// there is an accessor, remove descriptor.writable; | |
// populate descriptor.get and descriptor.set (IE's behavior) | |
delete descriptor.writable; | |
descriptor.get = descriptor.set = undefined; | |
if (getter) { | |
descriptor.get = getter; | |
} | |
if (setter) { | |
descriptor.set = setter; | |
} | |
return descriptor; | |
}; | |
} | |
if (!Object.defineProperties) { | |
Object.defineProperties = function (obj, props) { | |
var prop; | |
for (prop in props) { | |
if (hasOwnProp.call(props, prop)) { | |
Object.defineProperty(obj, prop, props[prop]); | |
} | |
} | |
}; | |
} | |
} | |
}()); | |
// Begin dataset code | |
if (!document.documentElement.dataset && | |
// FF is empty while IE gives empty object | |
(!Object.getOwnPropertyDescriptor(Element.prototype, 'dataset') || | |
!Object.getOwnPropertyDescriptor(Element.prototype, 'dataset').get) | |
) { | |
var propDescriptor = { | |
enumerable: true, | |
get: function () { | |
'use strict'; | |
var i, | |
that = this, | |
HTML5_DOMStringMap, | |
attrVal, attrName, propName, | |
attribute, | |
attributes = this.attributes, | |
attsLength = attributes.length, | |
toUpperCase = function (n0) { | |
return n0.charAt(1).toUpperCase(); | |
}, | |
getter = function () { | |
return this; | |
}, | |
setter = function (attrName, value) { | |
return (typeof value !== 'undefined') ? | |
this.setAttribute(attrName, value) : | |
this.removeAttribute(attrName); | |
}; | |
try { // Simulate DOMStringMap w/accessor support | |
// Test setting accessor on normal object | |
({}).__defineGetter__('test', function () {}); | |
HTML5_DOMStringMap = {}; | |
} | |
catch (e1) { // Use a DOM object for IE8 | |
HTML5_DOMStringMap = document.createElement('div'); | |
} | |
for (i = 0; i < attsLength; i++) { | |
attribute = attributes[i]; | |
// Fix: This test really should allow any XML Name without | |
// colons (and non-uppercase for XHTML) | |
if (attribute && attribute.name && | |
(/^data-\w[\w\-]*$/).test(attribute.name)) { | |
attrVal = attribute.value; | |
attrName = attribute.name; | |
// Change to CamelCase | |
propName = attrName.substr(5).replace(/-./g, toUpperCase); | |
try { | |
Object.defineProperty(HTML5_DOMStringMap, propName, { | |
enumerable: this.enumerable, | |
get: getter.bind(attrVal || ''), | |
set: setter.bind(that, attrName) | |
}); | |
} | |
catch (e2) { // if accessors are not working | |
HTML5_DOMStringMap[propName] = attrVal; | |
} | |
} | |
} | |
return HTML5_DOMStringMap; | |
} | |
}; | |
try { | |
// FF enumerates over element's dataset, but not | |
// Element.prototype.dataset; IE9 iterates over both | |
Object.defineProperty(Element.prototype, 'dataset', propDescriptor); | |
} catch (e) { | |
propDescriptor.enumerable = false; // IE8 does not allow setting to true | |
Object.defineProperty(Element.prototype, 'dataset', propDescriptor); | |
} | |
} |
<!DOCTYPE html> | |
<script src="html5-dataset.js"></script> | |
<h3 id="hd" data-ab-cd="zzz">Test!</h3> | |
<script> | |
var h = document.getElementById('hd'); | |
for (var i in h) { | |
if (i === 'dataset') {alert('iterated an Element object');} | |
} | |
for (var i in Element.prototype) { | |
if (i === 'dataset') {alert('iterated the Element prototype');} | |
} | |
try { | |
alert(h.dataset.abCd) | |
}catch(e) {alert(e);} | |
h.dataset.abCd = 'abc'; | |
alert(h.dataset.abCd) | |
</script> |
Thank you.
Doing some testing, this doesn't work properly in IE9.
This works:
el.setAttribute('data-test', 'test');
el.dataset.test = 'test2';
But this doesn't;
el.dataset.test = 'test';
el.dataset.test = 'test2';
This is happening because the second example never actually creates the data- attribute, so there's nothing to modify.
See this JSFiddle for details: http://jsfiddle.net/TXm66/1/
+1 to what @sslepian said, there is no way in IE 10 or IE 9 to simulate a dataset that behaves like the native one. Only an ES6 Proxy like would work, unfortunately the behavior of onpropertychange
even using attachEvent
in IE 10 or 9 won't behave like in IE8 … game over.
The simplest utility would be rather re-spec data
as I've done, as example, in eddy.js
Hi @brettz9 ,
I was wondering if you could let me know what license you have this gist under? I see that the 2 dependencies (Function.bind and Object.defineProperty/Object.getOwnPropertyDescriptor) are under the X11/MIT License, but I don't see a designation for the bit that you wrote. Is there any way that you might be able to update the Gist with a license?
Thanks in advance for your help!
Hi @spetz83 : I've made it more explicit now that X11/MIT applies to the whole document (with the Xccessors Object.defineProperty
polyfills being Eli Grey's).
Note that the limitation mentioned by @sslepian and @WebReflection on using dataset
against setting on the property does apply.
@brettz9 Thanks for updating. It is much appreciated.
I find that el.dataset.name
expression returns a value of String Object rather than string primitive value in IE9.
I've got a licensing question. How is this even supposed to be used? You refer to the license.md, which is not part of the gist. Is there a separate repo? I only need part that is related to the dataset polyfill, what do I do? Copy first 10 lines of the gist as a license?
The reference to license.md
is from the https://github.com/eligrey/Xccessors portion of the code (with his license at https://github.com/eligrey/Xccessors/blob/master/LICENSE.md ). You should be able to just attach this:
MIT license
Copyright © 2012-2018 Brett Zamir except for Xccessors code which is under the same license terms but is copyright © 2010 Elijah Grey, who also goes by Eli Grey.
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
Thank you