Расширяем сознание объекта

7 июля 2008 года, 23:27

Сегодня я хочу представить ещё две полезные proof-of-concept функции, которые используются мной достаточно часто (и очень облегчают жизнь, надо отметить). Я назвал их концептуальными, потому что не надо видеть в них знаки свыше, а воспринимать лишь как побуждение к размышлениями о бытье JavaScript-функциональности и расширяемости.

Создание иерархии элементов

Достаточно часто приходится в JavaScript создавать иерархию элементов. Сделать это можно двумя путями: традиционным и с помощью DOM. Традиционный путь представляет собой использование свойства innerHTML объекта HTMLElement, чтобы «на ходу» создавать иерархию HTML-элементов. Второй же путь является более правильным с философской точки зрения, так как обеспечивает использование DOM-методов для работы с DOM-деревом (он действительно может работать чуть медленнее, чем первый вариант, но если посмотреть на этот вопрос со стороны парсинга HTML, который снова и снова происходит при использовании свойства innerHTML, то становится даже понятным, почему вариант с DOM должен быть не медленнее варианта с innerHTML).

Поэтому нам нужна такая функция, которая по определённому пути создаст иерархию указанных элементов. Просто и полезно. Путь может быть, к примеру, таким: ul/li/a, чтобы создать список с одним элементом li с ссылкой внутри него. После этого мы можем добавлять новые элементы в список, используя путь li/.

Напомню, что для получения объекта по идентификатору я использую кастомный метод с короткой записью $:

function $(elid) { var element = document.getElementById(elid); return element; }

Попробуем создать этот метод:

function createPath(path) { //Проверяем начальный слеш и убираем его if (path.charAt(0) == "/") path = path.substr(1); //Разделяем путь по слешу var path = path.split("/"); //Устанавливаем текущий элемент в корневой var current_element = this; //Перебираем все элементы и создаём нужную иерархию for (var i = 0; i < path.length; i++) { //Пропускаем, если нет содержимого части пути if (path[i] == "") continue; //Создаём элемент var new_element = document.createElement(path[i]); //Добавляем элемент к DOM-дереву current_element.appendChild(new_element); //Устанавливаем новый элемент как текущий current_element = new_element; } //Возвращаем его return current_element; }

Теперь привяжем его к объектам:

//Для cultural-browsers Object.prototype.createPath = createPath; //Для других браузеров мы должны изменить $-функцию function $(elid) { var element = document.getElementById(elid); if (document.all) { element.createPath = createPath; } return element; }

Давайте возьмём простой HTML-код и изменим его с помощью нашей функции:

... <ul id="test"> <li>Old</li> </ul> ... <script type="javascript"> $("test").createPath("li/").innerHTML = "New"; </script> ...

Мы добавили элемент li к нашему списку и установили его содержимое в «New». Теперь у нас список из двух элементов.

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

Траверсинг элементов

Под траверсингом элементов подразумевается в нашем случае применение определённой функции-обработчика к большому количество элементов. Это бывает полезно, когда, например, необходимо добавить обработчик какого-либо действия на элементы, находящиеся в другом элементе (как, например, в списках).

Давайте реализуем её таким образом:

function eachChild(handler) { //Проходимся по каждому узлу for (var i = 0, ilength = this.childNodes.length; i < ilength; i++) { //Применяем обработчик, передаём элемент в обработчик handler(this.childNodes[i]); } }

Маленькая, но удобная. Добавим её к нашим объектам:

//Для cultural-browsers Object.prototype.eachChild = eachChild; //Меняем $-функцию function $(elid) { var element = document.getElementById(elid); if (document.all) { element.createPath = createPath; element.eachChild = eachChild; } return element; }

Попробуем её применить на нашем предыдущем обновлённом списке:

<ul id="test"> <li>Old</li> <li>New</li> </ul>

Добавим каждому элементу обработчик клика мышкой:

$("test").eachChild(function(element) { element.onclick = function() { alert(element.innerHTML); } });

Как получилось

А получилось, я думаю, просто, эстетично и практично. По крайней мере, мне они пришлись по вкусу. Не люблю фреймворки, а сделать свои рутины самостоятельно всегда приятно и полезно для осознавания принципов работы некоторых алгоритмов задач. Это всё на сегодня. Удачной вам новой недели.

Мнения (5)

Все эти хорошие люди уже прокомментировали запись. Поделитесь собственным мнением, расскажите, что вы думаете о поставленной проблеме, задаче, озвученных мыслях.

  • Miscђka

    08 июля 2008 г.09:10

    Ты очень интересно мыслишь :) Каждый раз узнаю из твоих статей про жабускрипт новые приемы, которые на трезвую голову и не придумаешь!

    В изменении функции $() не получится ли двойного присваивания для ослика? Он вроде нормально работает с prototype, а ты ему сверху еще навешиваешь if(document.all){}.

    Вообще, хотелось бы подробнее обсудить твою стратегию //Для cultural-browsers &other

  • Дин автор

    08 июля 2008 г.10:26

    Ой! Честно-честно, я не пью! :-)

    У меня в IE6 не работал способ, используемый для cultural-browsers, а таким способом заработало вполне.

    А для cultural-browsers мы просто меняем прототип объекта, хотя я здесь тоже сделал на самым лучшим образом и идеальным вариантом было бы указание прототипа для объекта рода HTMLElement.

  • Miscђka

    08 июля 2008 г.10:45

    Д > я не пью! :-)

    М > которые на трезвую голову и не придумаешь!

    Это я себя имел ввиду.

    > в IE6 не работал способ

    Под линухом? В венде что-то подобное я делал, все было нормально.

    > идеальным вариантом было бы указание прототипа для объекта рода HTMLElement

    :yes:

  • iwannt

    17 июля 2008 г.15:32

    "Траверсинг элементов" это супер!

    Если «большое количество элементов» считать коллекцией, то траверсинг — это что-то вроде итераторов :) По-сути, эдесь мы имеем дело с очень удачным и оправданным применением замыканий (или блоков, если вы Смолтокер или Руби-ист :) ).

    Пост на «пять»! «Получилось, я думаю, просто, эстетично и практично», мне тоже пришлось по вкусу :)

  • Дин автор

    17 июля 2008 г.16:21

    @iwannt, спасибо!

    Да, я действительно Руби-ист (рубироид даже, в некотором роде). ;-)

Я тоже знаю!

Для обращения к человеку используйте символ @, после которого следует имя того, к кому обращаетесь (пробелы заменяются на знак подчёркивания). Если вам интересно, можете подписаться на комментарии по RSS или по эл. почте. Ведите себя достойно, вы же не роботы, правда?

Вы можете использовать следующие XHTML-элементы в разметке комментария: strong, em, span[class=crossline], a[href=uri], code[type=язык], blockquote, ul и ol. В качестве языка кода может быть указан, например, javascript или css.