Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > JavaScript: Общие вопросы > Адаптер к W3C DOM - работаем тольк с тегами


Автор: Sardar 11.3.2005, 16:03
W3C DOM логичная штука, но не всегда удобно иметь теги в перемешку с тексовыми нодами, коментариями и прочим "шумом". Особенно это раздражает в мозилле, где пробелы между тегами это текстовые ноды. Приходится крутить циклы что бы добратся то тега.
В XML мы используем XPath для выборок нод, но как назло в HTML DOM сей фичи нет. Есть пара "левых" функций типа getElementsByTagName, getElementByName и прочие недоделки нормального XPath.

Представленный код очень прост, мы создаём отдельное дерево по дереву элементов, но в нашем дереве существуют только теги. Также добавим несколько функций выборок, которые часто необходимы.
Код
/** Человеческий DOM работающий только с тегами.
* @param node  - нода на которой строим дерево, спуск рекурсивный по потомкам
* @param pnod  - родительская нода
* @param psb   - previousSibling ноды
*/
function PDOM_Node(node, pnode, psb) {
  this.node=node; //ссылка на реальную ноду
  this.parentNode=pnode||null;
  this.style=node.style;
  this.id=node.id;
  this.nodeType=1; //tag
  this.tagName=node.tagName;
  node.pdom=this; //ссылка на себя

  this.childNodes=[];
  
  if(psb) psb.nextSibling=this;
  this.nextSibling=null;
  this.previousSibling=psb? psb: null;
  
  if(node.childNodes.length>0) {
     for(var i=0; i<node.childNodes.length; i++) {
        var nd=node.childNodes[i];
        while(nd.nodeType!=1 && nd.nextSibling) {nd=nd.nextSibling; i++; }
        if(nd.nodeType==1) {
          if(nd.pdom||nd instanceof PDOM_Node) this.childNodes.push(nd); //либо это PDOM нода либо дерево уже построенно
          else this.childNodes.push(new PDOM_Node(nd, this, ((this.childNodes.length>0)? this.childNodes[this.childNodes.length-1]: null)));
        } else break;
     }
  }
  this.firstChild=this.childNodes.length>0? this.childNodes[0]: null;
  this.lastChild=this.childNodes.length>0? this.childNodes[this.childNodes.length-1]: null; 
}
/** Выборки. Добавим второй параметр запрещающий рекурсивный спуск.
* Параметр recur разрешает рекурсивный спуск, иначе нода ищется
* среди прямых потомков */
PDOM_Node.prototype.getElementByClassName=function(name, recur) {
  for(var i=0; i<this.childNodes.length; i++) {
    if(this.childNodes[i].node.className.split(/\s+/)[0]==name) return this.childNodes[i];
    if(recur) {
      var ret=this.childNodes[i].getElementByClassName(name, recur);
      if(ret!=null) return ret;
    }
  } return null;
}
PDOM_Node.prototype.getElementByTagName=function(name, recur) {
  for(var i=0; i<this.childNodes.length; i++) {
    if(this.childNodes[i].node.tagName.toUpperCase()==name.toUpperCase()) return this.childNodes[i];
    if(recur) {
      var ret=this.childNodes[i].getElementByTagName(name, recur);
      if(ret!=null) return ret;
    }
  } return null;
}


Код очень прост. Теперь при работе с деревом нужно создать наше поддерево. PDOM дерево создаётся также и для потомков, ранее созданные деревья не создаются по новой. Ссылку на дерево хранить не нужно, ноды хранятся в свойствах "настоящих" нод в поле pdom.
Код
<div id="root">
  <div>Test</div>
  <div>bla <span class="test"> bla-bla</span> </div>
</div>
<script language="Javascript">
<!--
//создали поддерево
new PDOM_Node(document.getElementById("root"));

var n=document.getElementById("root")
var c=n.pdom.getElementByClassName("test",true) //ищем рекурсвно у детей, хотя 
//мне чаще нужно искать только среди непосредственных потомков
alert(c.node.innerHTML); //содержимое span
//-->
</script>


Ссылки на все часто используемые поля, например style, зеркально отображенны и в PDOM ноде. Это может сбить с толку, потому получив ноду проверьте что это:
Код
if(n instanceof PDOM_Node) {//это наша PDOM нода
  alert(n.node.innerHTML); // в поле node ссылка на настоящую ноду
} else { //это настоящая нода
  alert(n.pdom.firstChild); //в поле pdom ссылка на нашу PDOM ноду, через которую работаем только с тегами
}


Ничто не мешает реализовать XPath в ручную, но это будет довольно тяжёлый код, посик будет перебором дерева, т.е. медленный. Потому и реализовываем только то что нужно, например getElementsByClassName выбирающий все ноды с заданным аттрибутом class. Это редко нужно, в моём коде мы достаём первую попавшуюся на пути.

Этот код - просто идея, написан за 15мин, когда вдруг остро понадобилось smile Если идея ясна, то написать "под себя" не составит труда, иначе спрашивайте.

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)