Три функции, упрощающие оперирование DOM-узлами

Новички, только начиная писать на чистом JavaScript, как правило, не пишут ни каких дополнительных функций для упрощения манипуляций с элементами DOM, а зря. Это может существенно облегчить жизнь. Почему? Как минимум, потому, что названия функций становятся короче. Возмём, к примеру, очень часто используемый метод document.getElementById(id). Длинное название, не так ли? Можно уберечь себя от лишнего стука по клавишам сделав одну простую штуку:

function ge(id) {
	return document.getElementById(id);
}


Упростить себе жизнь можно не только сокращая название функций. JavaScript очень гибкий язык и при объявлении функции не нужно указывать тип аргумента, что иногда может сыграть с вами злую шутку. Например, думаешь, что складываешь числа, а на деле конкатенируешь строки. Но можно извлечь и выгоду из такой ситуации. Можно сделать так, чтобы функция корректно выполняла свою задачу при передаче ей значений разного типа. К примеру, очень полезно, чтобы функция работала одинаково при передаче DOM-узла или его id. Для этого можно немного улучшить нашу функцию ge(id):

/*
 * Если переданная строка или число, то, скорее всего, это id.
 * Получаем элемент и возвращаем его.
 * Если же передано что-то другое, то вероятно, это DOM-узел и
 * мы возвращаем его.
 */
 
function ge(obj) {
	return (typeof obj == ‘string’ || typeof obj == ‘number’) ?
 document.getElementById(obj) : obj;
}


и вызывать её в первых строках другой функции, которая что-то делает с DOM-узлом вот так:

function doSomethingWithNode(node) {
	var node = ge(node);
	// do something
}


Когда нужно оперировать со многими DOM-узлами, в качестве аргумента функции часто передают массив, состоящий из них. Но получать такой массив, даже используя нашу полезную функцию ge(id), не очень удобно. Примерно это выглядит так:

var arr = [ge(obj1), ge(obj2), ge(obj3)];


Можно упростить нам жизнь в такой ситуации. В JavaScript функция может получать произвольное количество аргументов, вне зависимости от того, сколько их было указано при её объявлении. При этом все аргументы хранятся в объекте arguments, который похож на массив. К n-ому аргументу функции можно обратиться так же, как к элементу массива: arguments[n]. Количество аргументов можно узнать тоже так же, как и количество элементов массива при помощи свойства length. Можно использовать это для того, что бы функция ge(obj) возвращала массив элементов в случае, когда ей передано несколько аргументов:

function ge(obj) {
	// Если передан один аргумент, то всё работает по-старому.
	if(arguments.lenght == 1) {
return (typeof obj == ‘string’ || typeof obj == ‘number’) ? document.getElementById(obj) : obj;
	// Если несколько
	} else {
		// Создаём массив
		var arr = [];
		for(var i = 0; i < arguments.length; ++i)
			// и забиваем его отобранными элементами
			arr[i] = ge(arguments[i]);
		return arr;
	}
}


На отборе элементов по id со странички операции с ними не заканчиваются. У отобранных элементов, как правило, меняют атрибуты или свойства и, если их много, то это выглядит немного гротескно:

input.className = ‘button’;
input.type = ‘submit’;
input.value = ‘Отправить’;
input.name = ‘go’;
input.id = ‘send’;


Сделать этот код читабельнее можно написав функцию, которая принимает в качестве аргументов DOM-узел (или id), объект (набор пар ключ (свойство) — значение), содержащий значения свойств и объект, содержащий значения атрибутов. Третий аргумент необходим, так как иногда изменение некоторых свойств не приводит к изменению атрибутов html-элемента. Для изменения значения атрибута используется метод setAttribute(‘attribute’, ‘value’). К свойству объекта в JavaScript можно обращаться как к элементу ассоциативного массива (obj[prop]). Это удобно, если имя свойства хранится в строковой переменной. Для того чтобы перебрать все свойства объекта используют конструкцию for(var prop in obj). Теперь нам известно всё, чтобы написать вот такую функцию:

function extend(el, props, attrs) {
	var el = ge(el);
	// Перебираем все свойства и присваиваем их элементу
	for(var p in props)
		el[p] = props[p];
	// С атрибутами так же
	for(var a in attrs)
		el.setAttribute(p, attrs[p]);
	return el;
}


И код, где меняется множество значений свойств одного и того же элемента запишется вот так:

extend(input, {
	className: 'button',
	type: 'submit',
	value: 'Отправить',
	name: 'go',
	id: 'send'
});


Если использовать функцию extend(el, props, attrs) можно хранить наборы значений свойств и атрибутов в переменных, что удобно, если эти наборы будут использоваться несколько раз. К тому же подобный формат записи хорошо знаком верстальщикам и им проще что-то менять чужом коде, вешая скрипт на свой сайт.

Создание DOM-узлов так же можно упростить, объединив создание узла с присвоением ему свойств и атрибутов. У нас уже есть функция extend(el, props, attrs) нам осталось только передать ей вновь созданный DOM-узел и наборы свойств и атрибутов.

function ce(tagName, props, attrs) {
	return extend(document.createElement(tagName), props, attrs);
}


Эти три функции, на мой взгляд, помогут сделать код понятнее и сократят его. Безусловно, для упрощения отбора элементов страницы и манипуляций над ними создано множество фреймворков (jQuery, prototype, ExtJS), но иногда приходится отказаться от их использования по разным причинам, к тому же изобретение собственного велосипеда способствует профессиональному росту.