Если бы существовал универсальный файл common.js, которым пользовались бы все разработчики, вы бы нашли там эти десять (плюс одна бонусная) функций.
UPDATE март 2010: Эта статья была обновлена и переписана, чтобы соответствовать сегодняшнему дню и общему уровню сайта.
Эти функции неоднократно были испытаны и доказали свою полезность всем тем, кто их использовал. Поэтому, без лишних вступлений, вот список из десяти, по моему мнению, величайших пользовательских функций на JavaScript, которые используются в настоящее время.
Несомненно, важнейший инструмент в управлении событиями! Вне зависимости от того, какой версией вы пользуетесь и кем она написана, она делает то, что написано у неё в названии: присоединяет к элементу обработчик события.
addEvent()
function addEvent(elem, evType, fn) {
if (elem.addEventListener) {
elem.addEventListener(evType, fn, false);
}
else if (elem.attachEvent) {
elem.attachEvent('on' + evType, fn)
}
else {
elem['on' + evType] = fn
}
}
Этот код обладает двумя достоинствами - он простой и кросс-браузерный.
Основной его недостаток - в том, он не передает this в обработчик для IE. Точнее, этого не делает attachEvent.
thisДля передачи правильного this можно заменить соответствующую строку addEvent на:
elem.attachEvent("on"+evType, function() { fn.apply(elem) })
Это решит проблему с передачей this, но обработчик никак нельзя будет снять, т.к. detachEvent должен вызывать в точности ту функцию, которая была передана attachEvent.
Существует два варианта обхода проблемы:
function addEvent(elem, evType, fn) {
if (elem.addEventListener) {
elem.addEventListener(evType, fn, false)
return fn
}
iefn = function() { fn.call(elem) }
elem.attachEvent('on' + evType, iefn)
return iefn
}
function removeEvent(elem, evType, fn) {
if (elem.addEventListener) {
elem.removeEventListener(evType, fn, false)
return
}
elem.detachEvent('on' + evType, fn)
}
Используется так:
function handler() {
alert(this)
}
var fn = addEvent(elem, "click", handler)
...
removeEvent(elem, "click", fn)
this в обработчике события вообще, а передавать элемент через замыкание:
function handler() {
// используем не this, а переменную, ссылающуюся на элемент
alert(*!*elem*/!*)
}
...
В качестве альтернативы и для примера более серьезной библиотеки обработки событий вы можете рассмотреть статью Кросс-браузерное добавление и обработка событий.
Для инициализации страницы исторически использовалось событие window.onload, которое срабатывает после полной загрузки страницы и всех объектов на ней: счетчиков, картинок и т.п.
Событие onDOMContentLoaded - гораздо лучший выбор в 99% случаев. Это событие срабатывает, как только готов DOM документ, до загрузки картинок и других не влияющих на структуру документа объектов.
Это очень удобно, т.к. картинки могут загружаться долго, а обработчик onDOMContentLoaded может произвести необходимые изменения на странице и инициализацию интерфейсов тут же, не дожидаясь загрузки всего.
Для добавления обработчика можно использовать следующий кроссбраузерный код:
function bindReady(handler){
var called = false
function ready() { // (1)
if (called) return
called = true
handler()
}
if ( document.addEventListener ) { // (2)
document.addEventListener( "DOMContentLoaded", function(){
ready()
}, false )
} else if ( document.attachEvent ) { // (3)
// (3.1)
if ( document.documentElement.doScroll && window == window.top ) {
function tryScroll(){
if (called) return
if (!document.body) return
try {
document.documentElement.doScroll("left")
ready()
} catch(e) {
setTimeout(tryScroll, 0)
}
}
tryScroll()
}
// (3.2)
document.attachEvent("onreadystatechange", function(){
if ( document.readyState === "complete" ) {
ready()
}
})
}
// (4)
if (window.addEventListener)
window.addEventListener('load', ready, false)
else if (window.attachEvent)
window.attachEvent('onload', ready)
/* else // (4.1)
window.onload=ready
*/
}
readyList = []
function onReady(handler) {
if (!readyList.length) {
bindReady(function() {
for(var i=0; i<readyList.length; i++) {
readyList[i]()
}
})
}
readyList.push(handler)
}
Использование:
onReady(function() {
// ...
})
Подробное описание функций bindReady, onReady и принципы их работы вы можете почерпнуть в статье Кроссбраузерное событие onDOMContentLoaded.
Изначально не написана никем конкретно. Многие разработчики писали свои собственные версии и ничья не показала себя лучше остальных.
Следующая функция использует встроенный метод getElementsByClass, если он есть, и ищет элементы самостоятельно в тех браузерах, где этого метода нет.
if(document.getElementsByClassName) {
getElementsByClass = function(classList, node) {
return (node || document).getElementsByClassName(classList)
}
} else {
getElementsByClass = function(classList, node) {
var node = node || document,
list = node.getElementsByTagName('*'),
length = list.length,
classArray = classList.split(/\s+/),
classes = classArray.length,
result = [], i,j
for(i = 0; i < length; i++) {
for(j = 0; j < classes; j++) {
if(list[i].className.search('\\b' + classArray[j] + '\\b') != -1) {
result.push(list[i])
break
}
}
}
return result
}
}
Например:
var div = document.getElementById("mydiv")
elements = getElementsByClass('class1 class2', div)
Следующие две функции добавляют и удаляют класс DOM элемента.
function addClass(o, c){
var re = new RegExp("(^|\\s)" + c + "(\\s|$)", "g")
if (re.test(o.className)) return
o.className = (o.className + " " + c).replace(/\s+/g, " ").replace(/(^ | $)/g, "")
}
function removeClass(o, c){
var re = new RegExp("(^|\\s)" + c + "(\\s|$)", "g")
o.className = o.className.replace(re, "$1").replace(/\s+/g, " ").replace(/(^ | $)/g, "")
}
Если быть честным, наверное для этой функции существует больше различных вариантов, чем было бы нужно.
Этот вариант никоим образом он не претендует на звание универсальной функции-"переключателя", но он выполняет основную функциональность показывания и спрятывания.
function toggle(el) {
el.style.display = (el.style.display == 'none') ? '' : 'none'
}
Обратите внимание, в функции нет ни слова про display='block', вместо этого используется пустое значение display=''. Пустое значение означает сброс свойства, т.е. свойство возвращается к значению, указанному в CSS.
Таким образом, если значение display для данного элемента, взятое из CSS - none (элемент спрятан по умолчанию), то эта функция toggle не будет работать.
Этот вариант функции toggle красив и прост, однако этот и некоторые другие недостатки делают его недостаточно универсальным. Более правильный вариант toggle, а также функции show и hide описаны в статье Правильные show/hide/toggle.
Как и getElementsByClass, этой функции почему-то нет в стандарте DOM. Возможно, чтобы избежать дублирования функционала, т.к. insertAfter реализуется всего одной строчкой.
function insertAfter(parent, node, referenceNode) {
parent.insertBefore(node, referenceNode.nextSibling);
}
Очень жаль, что это не часть встроенной функциональности DOM. Зато теперь у нас есть возможность всё время вставлять такие вот замечания!
Для поиска эта функция использует проверку ===, которая осуществляет поиск по точному сравнению, без приведения типов.
Метод Array.prototype.indexOf поддерживается не во всех браузерах, поэтому используется, если существует.
inArray = Array.prototype.indexOf ?
function (arr, val) {
return arr.indexOf(val) != -1
} :
function (arr, val) {
var i = arr.length
while (i--) {
if (arr[i] === val) return true
}
return false
}
В javascript нет способа нормально работать с cookie без дополнительных функций. Не знаю, кто проектировал document.cookie, но сделано на редкость убого.
Поэтому следующие функции или их аналоги просто необходимы.
// возвращает cookie если есть или undefined
function getCookie(name) {
var matches = document.cookie.match(new RegExp(
"(?:^|; )" + name.replace(/([\.$?*|{}\(\)\[\]\\\/\+^])/g, '\\$1') + "=([^;]*)"
))
return matches ? decodeURIComponent(matches[1]) : undefined
}
// уcтанавливает cookie
function setCookie(name, value, props) {
props = props || {}
var exp = props.expires
if (typeof exp == "number" && exp) {
var d = new Date()
d.setTime(d.getTime() + exp*1000)
exp = props.expires = d
}
if(exp && exp.toUTCString) { props.expires = exp.toUTCString() }
value = encodeURIComponent(value)
var updatedCookie = name + "=" + value
for(var propName in props){
updatedCookie += "; " + propName
var propValue = props[propName]
if(propValue !== true){ updatedCookie += "=" + propValue }
}
document.cookie = updatedCookie
}
// удаляет cookie
function deleteCookie(name) {
setCookie(name, null, { expires: -1 })
}
Аргументы:
byIdОна позволяет функции работать одинаково при передаче DOM-узла или его id.
function byId(node) {
return typeof node == 'string' ? document.getElementById(node) : node
}
Используется просто:
function hide(node) {
node = byId(node)
node.style.display = 'none'
}
function animateHide(node)
node = byId(node)
something(node)
hide(node)
}
Здесь обе функции полиморфны, допускают и узел и его id, что довольно удобно, т.к. позволяет не делать лишних преобразований node <-> id.
Надеюсь, этот небольшой и удобный список JavaScript-функций будет столь же полезен вам, сколь он полезен мне.