Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
155 views
in Technique[技术] by (71.8m points)

javascript - Modify prototypes of every possible DOM element

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 common 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 common 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)?

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

I 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?


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...