Текст ниже — перевод статьи "Unobtrusive Javascript", чей автор Christian Heilmann любезно дал мне согласие на перевод. Оригинальный адрес статьи www.onlinetools.org/articles/unobtrusivejavascript/chapter2.html
Для неопытных Javascript-разработчиков HTML — это площадка для игр.
HTML: <a href="index.html" onmouseover="image1.src='1on.gif'" onmouseout="image1.src='1off.gif'"> <img src="1off.gif" name="image1" border="0" height="150" width="150" alt="home"></a>
или если они чуть более продвинутые:
HTML: <a href="index.html" onmouseover="roll('home',1)" onmouseout="roll('home',0)"> <img src="home.gif" name="home" border="0" height="150" width="150" alt="home"></a> Javascript: // предзагрузка изображений homeoff = new Image(); homeoff.src = 'home.gif'; homeon = new Image(); homeon.src = 'homeoff.gif'; function roll(imgName,a) { imgState=a==0?eval(imgName + 'on.src'):eval(imgName + 'off.src'); document.images[imgName].src = imgState; }
В любом случае все вызовы событий находятся в HTML, и если имя функции поменяется, нам нужно будет внести изменения в каждом документе. Кроме того, каждый ролловер будет означать большее количество разметки, которая добавиться к общему весу страницы.
Давайте забудем на мгновение, что почти каждый эффект ролловера в наши дни скорее достигается с помощью CSS, чем с помощью Javascript, и допустим, что мы хотим использовать нижеследующую разметку и создать ролловер для изображения.
HTML: <a href="index.html"><img src="home.gif" id="home" alt="home"></a>
Итак, как сменить изображение, когда на него наводиться курсор мыши?
Каждый XML- (а также и HTML-) документ — это дерево узлов. Узел — часть этого дерева (представляйте файл или папку в проводнике Windows, когда вы просматриваете ваш диск). Узел может быть двенадцатью разными вещами, в HTML только три из них действительно интересны: element
, TextNode
и AttributeNode
.
Давайте посмотрим, какие функции и атрибуты мы можем использовать, чтобы перемещаться по дереву узлов документа, и как перепрыгивать от одного элемента до другого.
getElementById('elementID')
elementID
в виде объекта.getElementsByTagName('tag')
tag
в виде массива.Разумеется, мы можем смешивать и сочетать эти два варианта. Несколько примеров:
document.getElementById('navigation').getElementsByTagName('a')[3]; // возвращает четвёртую ссылку внутри элемента, // который имеет ID 'navigation' document.getElementsByTagName('div')[2].getElementsByTagName('p')[0]; // возвращает первый параграф внутри третьего div в документе.
childNodes
firstChild
и lastChild
, это сокращенные варианты childNodes[0]
и childNodes[this.childNodes.length-1]
.parentNode
nextSibling
previousSibling
Все это по желанию может быть смешано так, как нам нужно.
Javascript: var other=document.getElementById('nav').childNodes[3].firstChild; // возвращает четвёртый элемент первого подэлемента // внутри элемента с ID=nav var prevlink=o.parentNode.previousSibling.firstChild.childnodes[2]; // возвращает третий узел внутри предыдущего элемента, // того же уровня, что и родительский элемент элемента о.
attributes
data
nodeName
nodeType
nodeValue
null
, если это элемент.getAttribute (attribute)
attribute
.Javascript: var other=document.getElementById('nav').firstChild; if(other.nodeType==3) { other.data='newtext'; } if(other.nodeType==1) { other.firstChild.data='newtext'; }
Итак, чтобы достичь изображения в нашем примере, мы будем использовать либо getElementsByTagName
, либо getElementById
.
HTML: <a href="index.html"><img src="home.gif" id="home" alt="home"></a> Javascript: function findimg() { var image; image=document.getElementById('home'); if (image) { image.style.border='3px dashed #ccc'; } } //или: function findimg() { var imgs,i; imgs=document.getElementsByTagName('img'); for(i in imgs) { if(/home.gif/.test(imgs[i].src)) { imgs[i].style.border='3px dashed #ccc'; } } }
Использовать getElementById
гораздо проще, так как нам не нужно пробегать все элементы и искать уникальный идентификатор. В этом примере мы проверяем, содержит ли атрибут src
объекта изображения значение 'home.gif'. Более общий способ — проверить назначен ли элементу специальный класс.
HTML: <a href="index.html"><img src="home.gif" class="roll" alt="home"></a> Javascript: function findimg() { var imgs,i; imgs=document.getElementsByTagName('img'); for(i in imgs) { if(/roll/.test(imgs[i].className)) { imgs[i].style.border='3px dashed #ccc'; } } }
Итак, чтобы добавить эффект ролловера, все что нам надо сделать — это добавить функцию, которая переключает источник изображения и прикрепляет обработчик события к картинке.
function findimg() { var imgs,i; // перебираем все изображения в документе imgs=document.getElementsByTagName('img'); for(i=0;i<imgs.length;i++) { // проверяем существует ли класс 'roll' if(/roll/.test(imgs[i].className)) { // добавляем функцию roll на события onmouseover и onmouseout, //затем посылаем само изображение как объект imgs[i].onmouseover=function(){roll(this);}; imgs[i].onmouseout=function(){roll(this);}; } } } function roll(o) { var src,ftype,newsrc; // послать src изображения и найти расширение файла src = o.src; ftype = src.substring(src.lastIndexOf('.'), src.length); // проверить содержит ли источник изображения строку _on и, // если да - удалить её if(/_on/.test(src)) { newsrc = src.replace('_on',''); }else{ // в противном случае добавить _on к src newsrc = src.replace(ftype, '_on'+ftype); } o.src=newsrc; } window.onload=function(){ findimg(); }
Здесь можно проверить этот пример
Вроде бы все хорошо, но мы забыли одну вещь: хотя то, что получилось, просто замечательно, ролловер должен работать и без мыши. Чтобы достигнуть этого, мы должны проверить, находиться ли фокус на ссылке вокруг изображения, так как сама картинка, если на неё поставлена ссылка, недостижима с клавиатуры.
Чтобы сделать проверку, нам надо получить элемент, который содержит изображение, в данном случае — ссылку. Мы сделаем это с помощью команды parentNode
. Так как это заодно изменяет объект, которые передан как параметр функции roll()
, нам надо будет найти изображение снова. Поэтому мы перебираем дочерние узлы ссылок и смотрим, который из них одновременно и элемент, и изображение, проверив nodeType
and nodeName
. Это необходимо, так как некоторые браузеры рассматривают пробелы в источнике изображения как узел, другие же нет.
function findimg() { var imgs,i; // перебираем все изображения, проверяем содержат ли они класс roll imgs=document.getElementsByTagName('img'); for(i=0;i<imgs.length;i++) { if(/roll/.test(imgs[i].className)) { // добавляем функцию roll к родительскому элементу изображения imgs[i].parentNode.onmouseover=function(){roll(this);}; imgs[i].parentNode.onmouseout=function(){roll(this);}; imgs[i].parentNode.onfocus=function(){roll(this);}; imgs[i].parentNode.onblur=function(){roll(this);}; } } } function roll(o) { var i,isnode,src,ftype,newsrc,nownode; // перебираем все дочерние узлы for (i=0;i<o.childNodes.length;i++) { nownode=o.childNodes[i]; // если узел это и элемент, и IMG, // то установить значение переменной и выйти из цикла if(nownode.nodeType==1 && /img/i.test(nownode.nodeName)) { isnode=i; break; } } // проверить src и сделать ролловер src = o.childNodes[isnode].src; ftype = src.substring(src.lastIndexOf('.'), src.length); if(/_on/.test(src)) { newsrc = src.replace('_on',''); }else{ newsrc = src.replace(ftype, '_on'+ftype); } o.childNodes[isnode].src=newsrc; } window.onload=function(){ findimg(); }
Проверьте этот независимый от наличия мыши код
Просто скачайте демонстрационный HTML и попробуйте сделать одно из следующих заданий. Пройдите по ссылке рядом с заданием, чтобы увидеть одно из возможных решений. Решения, в качестве демонстрации, имеют встроенный Javascript, и код не выделяется в отдельный документ так, как это положено. Это сделано, чтобы помочь вам увидеть всю необходимую разметку в одном документе, это легче чем в двух разных.
window.location.hostname
) и добавьте href каждой ссылки в круглых скобках после ссылки. Пока вы ещё только изучаете, как это сделать правильно, используйте атрибут innerHTML
объекта ссылки, чтобы изменить его содержание. Решение для внешних ссылок.target
. Решение для всплывающих окон