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мин, когда вдруг остро понадобилось Если идея ясна, то написать "под себя" не составит труда, иначе спрашивайте. |