Измените прототипы всех возможных элементов DOM

Обновлен заголовок, чтобы лучше отражать то, что я пытаюсь сделать.

Короче говоря, существуют разные конструкторы для разных dom-элементов, и они, похоже, не имеют общего прототипа. Я ищу способ добавить свойство функции к каждому элементу DOM, изменив эти прототипы, но я не уверен, как их найти.

Например, я мог бы сделать что-то вроде этого:

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(){
    /* ... */
  } 
  /* ... */
});

Конечно, я бы хотел сделать это без перечисления каждого элемента html. Может ли кто-нибудь подумать о лучшем способе?

(Исходный вопрос следует)

Цель - сделать getElementsByClassName работать с любым DOM node в любом браузере.

Это было сделано раньше (вроде), но здесь мой снимок.

Вопрос, который у меня есть, есть хороший способ сделать эту работу с динамически создаваемыми элементами? Кажется, что элементы HTML DOM не имеют общего предсказуемого прототипа, где getElementsByClassName можно добавить... Или я что-то не хватает?

Вот что у меня до сих пор ( изменить - обновлено за обсуждение).

(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);
    }
  }

}());

Он хорошо работает с содержимым, загруженным до загрузки script, но новые динамически созданные элементы не получат метод getElementsByClassName. Любые предложения (помимо setInterval, пожалуйста)?

Ответ 1

Я думаю, что то, что вы хотите, может быть достигнуто путем прототипирования Element интерфейса, например

Element.prototype.getElementsByClassName = function() {
    /* do some magic stuff */
};

но не делают этого. Он не работает надежно во всех основных браузерах.

То, что вы делаете в примере в своем вопросе, тоже нецелесообразно. Вы фактически расширяете объекты хоста. Опять же, пожалуйста, не делайте этого.

Вы попадете именно в эти ловушки Прототип запущен.

Я не хочу просто копировать статью Kangax, поэтому, пожалуйста, прочитайте Что не так с расширением DOM.

Зачем вам это нужно? Какова ваша цель?

Ответ 2

Кажется, это работает, но это уродливо. Интересно, работает ли он в 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);
    }
  }

}());