Питання XML: По ту сторону DOM, HTML, XML, DHTML, Інтернет-технології, статті

Об’єктна модель документа, Document Object Model (DOM) – це один із самих реалізованих інструментів для роботи з даними XML і HTML, але його потенціал рідко використовується повністю. Якщо ви скористаєтеся моделлю DOM і зробите її простіше в застосуванні за допомогою розширень, ви отримаєте потужний інструмент для роботи з XML-додатками, в тому числі, з динамічними web-додатками.


Черговий матеріал у рубриці представляє запрошений провідний колонки, мій друг і колега, Дит Елза (Dethe Elza). У Діта великий досвід в розробці web-додатків, що працюють з XML, тому я запросив його, щоб розповісти про XML-програмуванні з використанням DOM і ECMAScript. Слідкуйте за новинами колонки, щоб не пропустити нових матеріалів Діта. -Девід Мертц (David Mertz)


DOM – це один із стандартних API для роботи з XML і HTML. Його часто критикують за надто марнотратне використання пам’яті, повільну роботу та / або за зайве багатослів’я. Однак для багатьох додатків це найправильніший вибір, і, безумовно, працювати з DOM простіше, ніж з іншим основним API для XML, SAX. DOM отримує все більше застосування в таких інструментах, як web-браузери, SVG-браузери, програми OpenOffice і багато інших.


DOM хороший тим, що він сам є прекрасно реалізованим стандартом, і крім того, вбудований в інші стандарти. Як стандарт, він завжди працює однаково, незалежно від мови програмування (Це може розцінюватися і як гідність, і як недолік, але, принаймні, має на увазі стабільність). В даний час DOM вбудовується в багато web-браузери і є компонентом багатьох специфікацій, заснованих на XML. Оскільки DOM і так вже є частиною вашого інструментарію і, ймовірно, ви хоча б час від часу їм користуєтеся, можливо, саме час скористатися перевагами, які пропонує ця модель.


Після того, як ви деякий час попрацюєте з DOM, у вас з’являться деякі шаблони – дії, які ви захочете використовувати знову і знову. Ярлики допоможуть вам обійти багатослівність DOM і створити зрозумілий і витончений код. Нижче представлена ​​колекція рад і прийомів, якими я користуюся частіше за все, з прикладами на JavaScript.


Методи insertAfter і prependChild


Для першого прийому не потрібно ніяких прийомів. В DOM є два методи, які додають дочірні вузли в вузол-контейнер (зазвичай Element, Але це може бути також Document або DocumentFragment): appendChild(node) і insertBefore(node, referenceNode). Але здається, тут чогось не вистачає. Що якщо мені захочеться вкласти вузол після опорного вузла або до дочірнього вузла (зробити новий вузол першим у списку)? Роками я писав допоміжні функції на зразок наступною:


Лістинг 1. Помилковий спосіб вставки і передумови





function insertAfter(parent, node, referenceNode) {
if(referenceNode.nextSibling) {
parent.insertBefore(node, referenceNode.nextSibling);
} else {
parent.appendChild(node);
}
}
function prependChild(parent, node) {
if (parent.firstChild) {
parent.insertBefore(node, parent.firstChild);
} else {
parent.appendChild(node);
}
}


Після виконання цього коду функція insertBefore() обов’язково повернеться до методу appendChild(), Якщо опорний вузол дорівнює нулю. Тому замість цього можна скористатися або кодом лістингу 2, Або взагалі відмовитися від цих методів і використовувати вбудовані функції:


Лістинг 2. Правильний спосіб вставки і передумови





function insertAfter(parent, node, referenceNode) {
parent.insertBefore(node, referenceNode.nextSibling);
}
function prependChild(parent, node) {
parent.insertBefore(node, parent.firstChild);
}


Якщо ви новачок в DOM-програмуванні, варто зазначити, що хоча в тій мові програмування, який ви обрали, може бути кілька покажчиків на вузол, в дереві DOM вузол може розміщуватися лише в одному місці. Тому при вставці вузла в дерево в іншому місці вам не доведеться спочатку видаляти вузол з дерева, це буде виконано автоматично. Це корисно в тому випадку, якщо ви захочете змінити порядок вузлів – Ви можете просто вставити вузол в нове місце.


З урахуванням цієї можливості, якщо у вас є два сусідніх вузла (назвемо їх node1 і node2), І вам потрібно перенести їх, скористайтеся одним з наступних виразів:






node1.parentNode.insertBefore(node2, node1);


або






node1.parentNode.insertBefore(node1.nextSibling, node1);


Що ще можна робити з DOM?


Оскільки існують web-сторінки, існує безліч варіантів застосування DOM. Якщо ви відвідаєте сайти, присвячені процедурам-закладок, “закладуркам” (bookmarklets) (див. розділ Ресурси), Ви можете виявити декілька невеликих сценаріїв, які втілюють альтернативне використання DOM для переформатування сторінок, вилучення посилань, приховування зображень або рекламних Flash-банерів і багато інших варіантів.


Але перше є перше. Оскільки Internet Explorer не описує постійні інтерфейсу Node, Які дозволяють легко ідентифікувати тип вузла, одна з основних функцій, яку можна виконувати в сценарії DOM для web-сайтів – це функція, що дозволяє переконатися, що ви задали тип вузла, який не відображається в браузері.


Лістинг 3. Сценарій, що дозволяє переконатися в тому, що вузол заданий





if (!window[“Node”]) {
window.Node = new Object();
Node.ELEMENT_NODE = 1;
Node.ATTRIBUTE_NODE = 2;
Node.TEXT_NODE = 3;
Node.CDATA_SECTION_NODE = 4;
Node.ENTITY_REFERENCE_NODE = 5;
Node.ENTITY_NODE = 6;
Node.PROCESSING_INSTRUCTION_NODE = 7;
Node.COMMENT_NODE = 8;
Node.DOCUMENT_NODE = 9;
Node.DOCUMENT_TYPE_NODE = 10;
Node.DOCUMENT_FRAGMENT_NODE = 11;
Node.NOTATION_NODE = 12;
}


В лістингу 4 показано, як отримати всі текстові вузли, що містяться в будь-якому вузлі:


Лістинг 4. Внутрішній текст





function innerText(node) {
// is this a text or CDATA node?
if (node.nodeType == 3 // node.nodeType == 4) {
return node.data;
}
var i;
var returnValue = [];
for (i = 0; i < node.childNodes.length; i++) {
returnValue.push(innerText(node.childNodes[i]));
}
return returnValue.join(“”);
}


Ярлики


Поширена претензія до DOM полягає в тому, що він занадто багатослівний і вимагає вводити занадто багато тексту для опису простих моментів. Наприклад, якщо ви хочете створити елемент <div>, Який містить певний текст, скажімо, у відповідь на натискання кнопки, код може виглядати приблизно так:


Лістинг 5. Створення елемента , довгий варіант





function handle_button() {
var parent = document.getElementById(“myContainer”);
var div = document.createElement(“div”);
div.className = “myDivCSSClass”;
div.id = “myDivId”;
div.style.position = “absolute”;
div.style.left = “300px”;
div.style.top = “200px”;
var text = “This is the first text of the rest of this code”;
var textNode = document.createTextNode(text);
div.appendChild(textNode);
parent.appendChild(div);
}


Якщо ви часто створюєте нові вузли цим способом, то зараз переконаєтеся в тому, що дуже скоро такий спосіб написання програм застаріє. Повинен бути кращий спосіб – і він є. Існує утиліта, яка допоможе створювати елементи, задавати для них атрибути, стилі і додавати текстові дочірні вузли. Всі аргументи за винятком name є необов’язковими.


Лістинг 6. Ярлик функції elem ()





function elem(name, attrs, style, text) {
var e = document.createElement(name);
if (attrs) {
for (key in attrs) {
if (key == “class”) {
e.className = attrs[key];
} else if (key == “id”) {
e.id = attrs[key];
} else {
e.setAttribute(key, attrs[key]);
}
}
}
if (style) {
for (key in style) {
e.style[key] = style[key];
}
}
if (text) {
e.appendChild(document.createTextNode(text));
}
return e;
}


Озброївшись цим ярликом, ви можете створити елемент <div> з лістингу 5 більш коротким способом. Зверніть увагу, що аргументи attrs і style задаються за допомогою літеральних об’єктів JavaScript.


Лістинг 7. Створення елемента скороченим способом





function handle_button() {
var parent = document.getElementById(“myContainer”);
parent.appendChild(elem(“div”,
{class: “myDivCSSClass”, id: “myDivId”},
{position: “absolute”, left: “300px”, top: “200px”},
“This is the first text of the rest of this code”));
}


Якщо ви хочете створювати більш складні об’єкти DHTML на льоту, то утиліти, подібні до цієї, можуть істотно заощадити час. Принцип такий: якщо ви збираєтеся часто створювати будь DOM-структури, то створіть утиліти, які будуть робити це за вас. Це не тільки зменшить кількість набирається коду, але також усуне повторні вирізки-вставки коду (а це основне джерело помилок) і зробить ваші наміри більш очевидними при читанні коду згодом.


Який вузол наступний?


В DOM не завжди просто визначити, який вузол є наступним у структурі документа. Ось деякі утиліти, які допоможуть вам переміщатися вперед і назад по вузлах:






// return next node in document order
function nextNode(node) {
if (!node) return null;
if (node.firstChild){
return node.firstChild;
} else {
return nextWide(node);
}
}
// helper function for nextNode()
function nextWide(node) {
if (!node) return null;
if (node.nextSibling) {
return node.nextSibling;
} else {
return nextWide(node.parentNode);
}
}
// return previous node in document order
function prevNode(node) {
if (!node) return null;
if (node.previousSibling) {
return previousDeep(node.previousSibling);
}
return node.parentNode;
}
// helper function for prevNode()
function previousDeep(node) {
if (!node) return null;
while (node.childNodes.length) {
node = node.lastChild;
}
return node;
}


На прогулянку з DOM


У деякий момент ви, ймовірно, захочете виконати обхід DOM, викликати функцію в кожному вузлі або повернути значення для кожного вузла. По суті, всім відомо, що стандарт DOM Level 2 включає розширення під назвою DOM Traversal and Range, яке задає об’єкти і API для ітерірованія через вузли DOM, проходить DOM, щоб застосувати функцію по всіх вузлах і вибирає діапазони DOM. Оскільки ця функція не визначається в Internet Explorer (як мінімум), для виконання подібних функцій можна використовувати метод nextNode().


Ідея цього методу – створити прості інструменти загального призначення, а потім комбінувати їх різними способами, щоб отримати бажані результати. Якщо ви знайомі з функціональним програмуванням, то дещо з описаного нижче здасться вам знайомим. Стаття “Beyond JS library” (За ту сторону JS) продовжує розвиток цієї ідеї.


Лістинг 9. Функціональні утиліти DOM





    / return an Array of all nodes, starting at startNode and
// continuing through the rest of the DOM tree
function listNodes(startNode) {
var list = new Array();
var node = startNode;
while(node) {
list.push(node);
node = nextNode(node);
}
return list;
}
// The same as listNodes(), but works backwards from startNode.
// Note that this is not the same as running listNodes() and
// reversing the list.
function listNodesReversed(startNode) {
var list = new Array();
var node = startNode;
while(node) {
list.push(node);
node = prevNode(node);
}
return list;
}
// apply func to each node in nodeList, return new list of results
function map(list, func) {
var result_list = new Array();
for (var i = 0; i < list.length; i++) {
result_list.push(func(list[i]));
}
return result_list;
}
// apply test to each node, return a new list of nodes for which
// test(node) returns true
function filter(list, test) {
var result_list = new Array();
for (var i = 0; i < list.length; i++) {
if (test(list[i])) result_list.push(list[i]);
}
return result_list;
}


Лістинг 9 містить чотири базових інструменту. Функції listNodes() і listNodesReversed() можуть бути розширені, щоб вони могли приймати додаткову довжину і працювати з методом Array slice(), Але цей експеримент я залишаю вам. Ще один момент, який варто відзначити – це те, що функції map() і filter() є функціями загального характеру, так як працюють з будь-яким списком, а не тільки зі списками вузлів. Далі показано кілька способів комбінування інструментів.


Лістинг 10. Застосування функціональних утиліт.





    // A list of all the element names in document order
function isElement(node) {
return node.nodeType == Node.ELEMENT_NODE;
}
function nodeName(node) {
return node.nodeName;
}
var elementNames = map(filter(listNodes(document),isElement), nodeName);
// All the text from the document (ignores CDATA)
function isText(node) {
return node.nodeType == Node.TEXT_NODE;
}
function nodeValue(node) {
return node.nodeValue;
}
var allText = map(filter(listNodes(document), isText), nodeValue);


Ці утиліти можна використовувати для витягання ідентифікаторів, зміни стилів, пошуку певних видів вузлів, які потрібно видалити, і так далі. Оскільки API DOM Traversal і Range реалізовані на багатьох платформах, їх можна використовувати для зміни дерева DOM без попереднього створення списку. Це дуже ефективні інструменти, принцип роботи яких аналогічний описаному вище.


Небезпечні зони DOM


Зверніть увагу на те, що ядро ​​API DOM не надає методів для синтаксичного розбору даних XML в DOM або зворотного сериализации моделі DOM з XML. Ці методи описані в розширенні для DOM Level 3, “Load and Save”, але це розширення не реалізовано в достатній мірі, щоб можна було на нього розраховувати. Кожна платформа (браузер або іншу програму, що вміє працювати з DOM) має свої методи конверсії XML в DOM і з DOM, але в цій статті не розглядається, як можна робити це на різних платформах.


DOM не цілком надійний інструмент – зокрема, за допомогою API DOM можна створити дерево, яке неможливо буде серіалізовать в XML. Ніколи не використовуйте в одній програмі API DOM1, не підтримують простору імен, та їх аналоги з DOM2, які підтримують простір імен, (наприклад, createElementcreateElementNS). Якщо ви використовуєте простору імен, спробуйте зберігати всі ваші оголошення просторів імен в кореневому елементі, і ніколи не міняйте префікси просторів імен, тому що це абсолютно згубний шлях. Загалом, якщо у вас досить здорового глузду, ви не будете провокувати крайні ситуації, які можуть привести до виникнення проблем.


Якщо вам доводиться для виконання синтаксичного розбору покладатися на функції innerText і innerHTML Internet Explorer, краще спробуйте замість них використовувати функцію elem(). Створивши невелику кількість аналогічних утиліт, ви можете отримати максимум зручності, але при цьому зберегти кроссплатформенность коду. Але ні в якому разі не можна змішувати ці два підходи.


Окремі символи Unicode не можуть бути включені в XML. Реалізація DOM дозволяє зробити це, але результат буде несеріалізуемим. Він буде включати більшість керуючих символів і окремі символи з пар для заміни символів Unicode. Ви можете зіткнутися з цим тільки в одному випадку: якщо спробуєте включити в документ двійкові дані, але це, можливо, інша проблемна ситуація.


Висновок


У статті розглядається безліч основних прийомів, але DOM (і JavaScript) можуть зробити для вас набагато більше. Вивчіть матеріал, розгляньте приклади і ви зрозумієте, як можна застосувати ці принципи для вирішення проблем, які інакше зажадали б застосування виконання сценаріїв на стороні клієнта, шаблонів або спеціальних API.


У DOM є обмеження і недоліки, але є й багато переваг: Ця модель вбудована в багато програми; працює незалежно від того, який технологією ви користуєтеся – Java, Python або JavaScript, більше зручна, ніж SAX; а завдяки маніпуляціям, продемонстрованим вище, може бути елегантною і ефективної в застосуванні .. Більша частина програм останнім часом починають підтримувати DOM, в тому числі, додатки на основі Mozillа, OpenOffice і XMetaL від Blast Radius. Більшість специфікацій вимагають наявності DOM і створюють для цієї моделі розширення (такі, як SVG), тому найближчим часом DOM не зійде зі сцени. Непогано було б познайомитися з цим використовуються на багатьох платформах інструментом.

Схожі статті:


Сподобалася стаття? Ви можете залишити відгук або підписатися на RSS , щоб автоматично отримувати інформацію про нові статтях.

Коментарів поки що немає.

Ваш отзыв

Поділ на параграфи відбувається автоматично, адреса електронної пошти ніколи не буде опублікований, допустимий HTML: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

*

*