Не так давно я начал заниматься разработкой JavaScript framework'а «js-core» для применения в небольших проектах, в которых использование таких готовых универсальных решений, как «Prototype», «MooTools» или «jQuery» является не оправданным, в связи с достаточно большим объемом кода, большая часть которого не используется. Framework получился легкий (в несжатом виде версия 2.5.1 занимает около 15кб), но в тоже время достаточно функциональным.
При разработке «js-core» я ориентировался на опытных программистов, поэтому постарался исключить как можно больше лишних проверок. Например, «jQuery» может автоматически подставить вместо переданного параметра «class
» значение «className
», либо позволить ввести сразу «className
», в связи с чем, каждый раз при использовании метода «attr
» идет проверка и подстановка соответствующего значения. Так же в «js-core» активно применяется расширение стандартных прототипов встроеных объектов, поэтому не забывайте делать проверку «hasOwnProperty
» в конструкиции «for(…in…)
». Чтобы не делать такую проверку в framework'е предумотрены методы «object.each(fn, [arg])
», «$.foreach(object, fn)
», а также реализован метод «array.forEach(fn, context)
» для старых браузеров, не поддерживающих его.
Для начала, рассмотрим пример, демонстрирующий упрощенный принцип работы JavaScript framework'а.
// Глобальный объект с набором частоиспользуемых
// функций, на основе которых построено большинство
// методов framework'а.
var core = {
// Определение версии «Internet Explorer»
ie: 0 /*@cc_on + @_jscript_version * 10 % 10 @*/,
// …
// и другие атрибуты и методы.
// …
};
// Главная функция-конструктор.
function _$(arg) {
// В атрибут «node» нового объекта запоминаем ссылку на DOM-узел
this.node = typeof arg == 'string' ? document.getElementById(arg) : arg;
}
// Методы работы с DOM-узлами,
// доступные через прототип функции «_$».
_$.prototype = {
parent: function() {
// Возвращаем новую копию объекта «_$»
// с сылкой на родительский узел
// текущего элемента.
return new _$(this.node.parentNode);
},
html: function(html) {
// Если указан входной параметр «html»,
// то заменяем «innerHTML» текущего узла
// на переданный во входном параметре.
if(typeof html != 'undefined') {
this.node.innerHTML = html;
return this;
}
// Иначе возвращаем «innerHTML» текущего узла.
else return this.node.innerHTML;
},
// …
// здесь же и все остальные методы.
// …
};
// Функция, сокращающа запись создания
// новой копии объекта «_$».
function $(arg) {
return new _$(arg);
}
// Дополнительные методы
$.n = function(tag) {
// Возвращаем новую копию объекта «_$»
// с сылкой на новый узел с именем тега,
// указанным в параметре «tag».
return new _$(document.createElement(tag));
};
Как видим, каждый раз во время выполнения функции «$
» происходит конструирование нового объекта, который содержит всего одну ссылку «node» на DOM-узел, но через прототип функции-конструктора «_$
» для него доступен набор методов, работающих с текущим узлом по этой ссылке.
В «js-core» реализовано кэширование для ссылок на DOM-узлы, полученных по идентификатору. То есть метод «document.getElementById
» выполняется всего один раз для каждого запрашиваемого узла с идентификатором. Это позволяет повысить производительность в некоторых ситуациях.
В весрии 2.5.1 реализовано более 50-и методов, позволяющих JavaScript-программисту сразу приступить к написанию веб-приложения, не затрачивая время на подготовку основы для его работы. Рассмотрим некоторые из них.
$(…)
Для получения новой копии объекта «_$
» используется функция «$(…)
», которая может принимать строку, содержащую идентификатор или ссылку на DOM-узел. Допустим, у нас есть узел <DIV>
с идентификатором «test», тогда мы можем выполнить следующее:
var obj = $('test');
// или
var node = document.getElementById('test');
var obj = $(node);
Далее, чтобы сократить запись, запомним, что все функции, возвращающие один узел, возвращают его в обертке — новой копии объекта «
_$
» c атрибутом «node», содержащим ссылку на этот DOM-узел. А функции, возвращающие набор значений, отдают нумерованный массив или «DOMNodeList
» ссылок на узлы. Некоторые функции (css
,attr
) могут возвращать как одиночные значения, так и массивы, в зависимости от количества входящих параметров.
exist(…)
В отличие от «jQuery» в «js-core» не проверяется каждый раз наличие элемента, поэтому если, попытаться выполнить некоторые методы с несуществующим узлом, произойдет ошибка. Для случаев, когда элемент может отсутствовать, предусмотрен метод «exist
», который может принимать в качестве входных параметров 2 функции: 1-я выполнится, если указанный элемент существует, 2-я выполнится, если такой элемент отсутствует.
$('test').exist(
function() {
// По ссылке «this» доступен текущий DOM-узел
$(this)…
},
function() {
alert('Элемент отсутствует');
}
);
Так же функцию «exist» можно вызывать без параметров. В этом случае она вернет логическое значение «true» или false».
if($('test').exist()) {…}
append(…)
и prepend(…)
Для добавления нового дочернего узла или перемещения уже существующего в список дочерних элементов текущего узла используются функции «append(…)
» и «prepend(…)
». Первая функция добавляет элемент в конец списка дочерних элементов, вторая — в начало.
$('test').append('h1');
// или
var node = document.createElement('h1');
$('test').append(node);
// а так же
var node = document.getElementById('some-id');
$('test').append(node);
html(…)
и text(…)
Можно продолжить предыдущую цепочку и сразу же добавить в новый дочерний узел какой-нибудь текст или HTML-код:
$('test').append('h1').text('Hello World!');
// или
$('test').append('h1').html('<span>Hello</span> World!');
Для тех, кто хорошо знает «jQuery» сразу становится видно отличие «js-core» в работе с элементами в пределах одной цепочки вызовов. В «jQuery» метод «html
» заменил бы «innerHTML
» DOM-элемента с идентификатором «test», а в «js-core» работа ведется с последним используемым узлом, в данном примере это заголовок первого уровня <H1>
.
after(…)
и before(…)
Создают новый узел после (перед) текущим или переносят существующий.
$('test').after('p').text('example');
$('test').after(document.getElementById('some-id'));
$('test').before(document.createElement('span')).html('example');
appendTo(…)
и prependTo(…)
Переносят текущий узел в конец (начало) списка дочерних элементов узла, переданного в качестве входного параметра (ссылка на DOM-элемент или строка, содержащая идентификатор узла).
$('test').appendTo('some-id');
$('test').prependTo(document.body);
insertAfter(…)
и inserBefore(…)
Ставят текущий узел после (перед) указанного узла (ссылка на DOM-элемент или строка, содержащая идентификатор узла).
$('test').insertAfter(document.getElementsByTagName('div')[0]);
$('test').inserBefore('some-id');
next(…)
и prev(…)
Возвращают первый соседний справа или слева элемент. В качестве входного параметра может приниматься строка и именем тега. Если входной параметр не указан, то возвращается первый соседний узел с «nodeType == 1
».
$('test').next('blockquote');
$('test').prev();
Если соседний элемент отсутствует, функция вернет «undefined».
child(…)
Возвращает массив дочерних элементов. Для фильтрации элементов в качестве первого параметра функции можно указать строку или массив строк, содержащих имена тегов. Если указан второй параметр «true», то поиск элементов производится по всем вложенным узлам.
$('test').child('input select textarea', true);
$('test').child(['div', 'p']);
parent(…)
Возвращает родительский элемент текущего узла.
$('test').parent();
remove(…)
и empty(…)
Удаляют и очищают текущий узел соответственно.
$('test').remove();
$('test').empty();
Заметим, что функция «empty
» возвращает текущую копию объекта «_$
», поэтому мы можем продолжить работу с узлом.
$('test').empty().append('h1').text('example');
Полный перечень методов «js-core» можно посмотреть на главной странице проекта. Для каждой функции указаны все возможные комбинации параметров. В ближайшее время постараюсь сделать, что-то вроде «sheatsheet» по всем функциям.
Сейчас framework используется здесь и на сайте «ЗАО КЛФЗ».