Last active
April 29, 2023 14:58
-
-
Save brettz9/4093766 to your computer and use it in GitHub Desktop.
Dataset Shim
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* 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); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!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> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
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: