admin管理员组文章数量:1431398
Updated title to better reflect what I'm trying to do.
In short, there are different constructors for different dom elements, and they don't seem to all share a mon prototype. I'm looking for a way to add a function property to every DOM element by modifying these prototypes, but I'm not sure how to find them.
For example, I could do something like this:
function enhanceDom (tagNames, methods) {
var i=-1, tagName;
while (tagName=tagNames[++i]) {
var tag=document.createElement(tagName);
if (!(tag && tag.constructor)) continue;
for (var methodName in methods) {
tag.constructor.prototype[methodName]=methods[methodName];
}
}
}
var thingsToEnhance = ['a','abbr','acronym','address'/* on and on... */];
enhance(thingsToEnhance, {
doStuff : function(){
/* ... */
},
doOtherStuff : function(){
/* ... */
}
/* ... */
});
Of course, I'd like to do this without listing every single html element. Can anyone think of a better way?
(Original question follows)
Goal - make getElementsByClassName
work on any DOM node in any browser.
It's been done before (sort of), but here's my shot at it.
The question I have is, is there a good way to make this work with dynamically created elements? It seems that HTML DOM elements don't share a mon predictable prototype where getElementsByClassName
could be added... Or am I missing something?
Here's what I've got so far (edit - updated per discussion).
(function(){
var fn = 'getElementsByClassName';
// var fn = 'gEBCN'; // test
if (typeof document[fn] != 'undefined') return;
// This is the part I want to get rid of...
// Can I add getByClass to a single prototype
// somewhere below Object and be done with it?
document[fn]=getByClass;
withDescendants(document, function (node) {
node[fn]=getByClass;
});
function withDescendants (node, callback, userdata) {
var nodes = node.getElementsByTagName('*'), i=-1;
while (node=nodes[++i]) {
callback(node, userdata);
}
return userdata;
}
function getByClass (className) {
return withDescendants(this, getMatches, {
query:new RegExp('(^|\\s+)' + className + '($|\\s+)'),
found:[]
}).found;
}
function getMatches (node, data) {
if (node.className && node.className.match(data.query)) {
data.found.push(node);
}
}
}());
It works well on content loaded before the script loads, but new dynamically-created elements won't get a getElementsByClassName
method. Any suggestions (besides setInterval, please)?
Updated title to better reflect what I'm trying to do.
In short, there are different constructors for different dom elements, and they don't seem to all share a mon prototype. I'm looking for a way to add a function property to every DOM element by modifying these prototypes, but I'm not sure how to find them.
For example, I could do something like this:
function enhanceDom (tagNames, methods) {
var i=-1, tagName;
while (tagName=tagNames[++i]) {
var tag=document.createElement(tagName);
if (!(tag && tag.constructor)) continue;
for (var methodName in methods) {
tag.constructor.prototype[methodName]=methods[methodName];
}
}
}
var thingsToEnhance = ['a','abbr','acronym','address'/* on and on... */];
enhance(thingsToEnhance, {
doStuff : function(){
/* ... */
},
doOtherStuff : function(){
/* ... */
}
/* ... */
});
Of course, I'd like to do this without listing every single html element. Can anyone think of a better way?
(Original question follows)
Goal - make getElementsByClassName
work on any DOM node in any browser.
It's been done before (sort of), but here's my shot at it.
The question I have is, is there a good way to make this work with dynamically created elements? It seems that HTML DOM elements don't share a mon predictable prototype where getElementsByClassName
could be added... Or am I missing something?
Here's what I've got so far (edit - updated per discussion).
(function(){
var fn = 'getElementsByClassName';
// var fn = 'gEBCN'; // test
if (typeof document[fn] != 'undefined') return;
// This is the part I want to get rid of...
// Can I add getByClass to a single prototype
// somewhere below Object and be done with it?
document[fn]=getByClass;
withDescendants(document, function (node) {
node[fn]=getByClass;
});
function withDescendants (node, callback, userdata) {
var nodes = node.getElementsByTagName('*'), i=-1;
while (node=nodes[++i]) {
callback(node, userdata);
}
return userdata;
}
function getByClass (className) {
return withDescendants(this, getMatches, {
query:new RegExp('(^|\\s+)' + className + '($|\\s+)'),
found:[]
}).found;
}
function getMatches (node, data) {
if (node.className && node.className.match(data.query)) {
data.found.push(node);
}
}
}());
It works well on content loaded before the script loads, but new dynamically-created elements won't get a getElementsByClassName
method. Any suggestions (besides setInterval, please)?
- Is there some reason you've decided not to use a framework such as jQuery for this? If so, you might want to state that in your question or you'll get lots of "use jQuery!" answers. – Greg Hewgill Commented Sep 6, 2010 at 19:19
- I hope it will be obvious that I don't intend to use jQuery for this. Fingers crossed. – Dagg Nabbit Commented Sep 6, 2010 at 19:21
- 1 @Greg With no JQuery code in the posted code and the no presence of JQuery tag, i think is enough... – Garis M Suero Commented Sep 6, 2010 at 19:22
- Perhaps you should have a look at Sizzle github./jeresig/sizzle/blob/master/sizzle.js – Caspar Kleijne Commented Sep 6, 2010 at 20:21
- 3 (insert obligatory "use jQuery!" answer here) – Pekka Commented Sep 6, 2010 at 21:56
2 Answers
Reset to default 5I think what you want can be achieved by prototyping the Element
interface, like
Element.prototype.getElementsByClassName = function() {
/* do some magic stuff */
};
but don't do this. It doesn't work reliably in all major browsers.
What you're doing in the example in your question is not advisable, too. You're actually extending host objects. Again, please don't do this.
You'll fall in exactly those pitfalls Prototype ran into.
I don't want to merely copy Kangax' article, so please read What’s wrong with extending the DOM.
Why do you want this in the first place? What's your goal?
This seems to work, but it's ugly. I wonder if it works in IE?
(function(){
enhanceDom('a abbr acronym address applet area b base basefont bdo big blockquote body br button caption center cite code col colgroup dd del dfn dir div dl dt em fieldset font form frame frameset h1 h2 h3 h4 h5 h6 head hr html i iframe img input ins isindex kbd label legend li link map menu meta noframes noscript object ol optgroup option p param pre q s samp script select small span strike strong style sub sup table tbody td textarea tfoot th thead title tr tt u ul var'
,{
getElementsByClassName : getByClass
/* , ... */
});
function enhanceDom (tagNames, methods) {
var i=-1, tagName;
if (tagNames==''+tagNames) {
tagNames=tagNames.split(' ');
}
for (var methodName in methods) {
setIfMissing(document, methodName, methods[methodName]);
while (tagName=tagNames[++i]) {
var tag=document.createElement(tagName);
if (tag || !tag.constructor) continue;
var proto=tag.constructor.prototype;
setIfMissing(proto, methodName, methods[methodName]);
}
}
}
function setIfMissing (obj, prop, val) {
if (typeof obj[prop] == 'undefined') {
obj[prop]=val;
}
}
function withDescendants (node, callback, userdata) {
var nodes=node.getElementsByTagName('*'), i=-1;
while (node=nodes[++i]) {
callback(node, userdata);
}
return userdata;
}
function getByClass (className) {
return withDescendants(this, getMatches, {
query:new RegExp('(^|\\s+)' + className + '($|\\s+)'),
found:[]
}).found;
}
function getMatches (node, data) {
if (node.className && node.className.match(data.query)) {
data.found.push(node);
}
}
}());
本文标签: javascriptModify prototypes of every possible DOM elementStack Overflow
版权声明:本文标题:javascript - Modify prototypes of every possible DOM element - Stack Overflow 内容由网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://www.betaflare.com/web/1745571773a2664090.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论