Кроссбраузерный DHTML
Начинающие веб-программисты рано или поздно сталкиваются с тем, что их скрипт, любовно написанный (или позаимствованный) и прекрасно работающий на домашнем компьютере с MSIE 5.5, почему-то не работает у соседа или клиента на его Opera, Mozilla или Netscape.
Цель настоящей статьи - рассмотреть имеющиеся различия в DHTML-объектах и функциях у нынешних браузеров. Предполагается, что читатель уже более-менее знаком с программированием по крайней мере под один из браузеров.
Оглавление
- Определение браузера
- Окна и документы
- Формы
- Доступ к формам
- Формы и Mozilla
- и Netscape и Opera
- Картинки
- Слои
- Доступ к слою
- Доступ к CSS-свойствам слоя
- Различия в CSS-свойствах
- Кроссбраузерная прокрутка
- Не CSS-свойства
- Изменение содержимого слоя
- Динамическое создание слоев через do*****ent.write в Netscape 4
- Мышь
События мыши (пока не готово)-->
- Отслеживание координат мыши
Определение браузера
Браузеров и их версий довольно много, но многие из них не различаются между собой пониманием DHTML. Поэтому есть смысл разбить их на несколько групп, названных по наиболее распространенному представителю. Проверять на принадлежность к той или иной группе будем через поддержку испытуемым браузером тех или иных объектов:
isDOM=do*****ent.getElementById //DOM1 browser (MSIE 5+, Netscape 6, Opera 5+)
isMSIE=do*****ent.all && do*****ent.all.item //Microsoft Internet Explorer 4+
isNetscape4=do*****ent.layers //Netscape 4.*
isOpera=window.opera //Opera
isOpera5=isOpera && isDOM //Opera 5+
isMSIE5=isDOM && isMSIE //MSIE 5+
isMozilla=isNetscape6=isDOM && !isMSIE && !isOpera //Mozilla или Netscape 6.*
Последняя строчка спорна, т. к. не обязательно все, что не Опера и не MSIE - Mozilla. Однако если появятся браузеры, которые не совместимы ни с MSIE, ни с Opera, то с чем же им еще быть совместимыми, как не с Mozilla (Netscape 6)? Больше не с кем. Для особо ответственных случаев, когда надо наверняка отбросить все неизвестные браузеры, можно воспользоваться тем, что все современные Mozillы содержат в navigator.appName строчку "Netscape", т.е.
isMozilla=isNetscape6=isDOM && (navigator.appName=="Netscape")
Окна и документы
В разных браузерах по-разному вызываются такие свойства, как размеры окна, размеры документа, показатели прокрутки и т.д.
Размеры рабочей области окна
- MSIE 4+ - do*****ent.body.clientWidth, clientHeight
- Netscape, Mozilla, Opera - innerWidth, innerHeight
Координаты верхнего левого угла окна
- MSIE 4+ - screenLeft, screenTop
- Netscape, Mozilla, Opera - screenX, screenY>
Размеры содержимого документа
- MSIE 4+ - do*****ent.body.scrollWidth, scrollHeight
- Netscape, Mozilla - do*****ent.width, height
- Opera - do*****ent.body.style.pixelWidth, pixelHeight
Прокрутка (scrolling)
- MSIE 4+ - do*****ent.body.scrollLeft, scrollTop
- Netscape, Mozilla, Opera - pageXOffset, pageYOffset
В MSIE в документе должен присутствовать тег , иначе do*****ent.body не будет определено.
ФормыДоступ к формам
В MSIE такой код прекрасно работает:
......
myform.mytext.value="hello"
Но в 4-ом Netscape этот код выдает ошибку. Там нельзя так обращаться к формам. Необходимо писать do*****ent.myform.mytext.value="hello" - это будет работать и там, и там. Чтобы избежать возможных конфликтов имени формы с другими объектами do*****ent лучше писать do*****ent.forms.myform.mytext.value или do*****ent.forms["myform"].elements["mytext"].value. Последняя запись рекомендуется в случаях, когда имя формы или ее элемента могут содержать недопустимые для имени переменной в JavaScript символы.
Формы и Mozilla
В Mozilla лучше не обращаться к элементам страницы (особенно формам) до наступления события onload.
и NetscapeВ Netscape у объекта select нет свойства value. Для выбора нужного option пользуйтесь проверкой select.options[n].value и установкой соответствующего select.selectedIndex. Пользоваться select.options[n].selected=true/false не рекомендуется из-за проблем с Opera (см. далее). и Opera
В Opera старых версий (напр. 5.10, в отличие от, к примеру, 5.12) нельзя выбрать опцию select через select.options[n].selected=true. Вместо этого нужно писать select.selectedIndex=n.
Картинки
Доступ с картинкам, т. е. объектам, создаваемым тегом , осуществляется через коллекцию do*****ent.images[]. Но у Netscape 4 есть особенность в вызове картинки, если она вставлена в слой (см. ниже).
Слои
Если в MSIE 4+ и Mozilla (он же Netscape 6) слоем может быть любой элемент страницы, то в Netscape 4 и Opera 5 это обычно контейнер
с определенным стилями absolute или relative расположением. Некоторые рекомендуют использовать в Netscape 4 тег , т. к. он лучше понимается Нетскейпом. Поэтому те случаи, когда
и в 4-ом Netscape ведут себя по-разному, возможно, я рассмотрю в одной из будущих статей. Но эти случаи достаточно редки и специфичны и связаны с глюками в форматировании содержимого слоев.
В Netscape 4 не используйте в именах классов (class=) и идентификаторов (id=) символ подчеркивания "_", в противном случае Netscape не увидит этого элемента.
Доступ к слою
Доступ к слоям по-разному осуществляется в разных браузерах. А именно:
- MSIE 4+ - do*****ent.all[layerName]
- Netscape 4 - do*****ent.layers[layerName]
- DOM1 (MSIE 5+, Mozilla, Opera 5) - do*****ent.getElementById(layerName)
Можно порекомендовать такую функцию:
function layer(layerName){
//DOM1
if(do*****ent.getElementById) return do*****ent.getElementById(layerName)
//MSIE4
if(do*****ent.all) return do*****ent.all[layerName]
//Netscape 4
if(do*****ent.layers) return do*****ent.layers[layerName]
//неподдерживаемый браузер
return null
}
С доступом к слоям в Netscape 4 имеется один аспект. От связан с вложенными (nested) слоями, т. е. со слоями, которые описаны внутри контейнеров другого слоя. Пример (предполагается, что в CSS для тегов
задано допустимое свойство position):
xxx
Если в MSIE 4+ можно вызвать слой "cool" через do*****ent.all["cool"], то в Netscape 4 do*****ent.layers["cool"] вернет undefined. Для вызова вложенного слоя придется написать такую конструкцию:
do*****ent.layers["mylayer"].do*****ent.layers["cool"]
То же самое касается и адресации других объектов, находящихся внутри слоя - картинок, форм, ссылок и т.д.
Стало быть, нашу функцию можно модернизировать таким образом:
//рекурсивный поиск по слоям
function findLayer(what, where){
if(!where) return
var i,l,parent
var len=where.length
for(i=0;iparent=where[i].do*****ent.layers
l=parent[what]
if(l) return l
l=findLayer(what, parent)
}
return false
}
function layer(layerName, parentLayerName){
if(do*****ent.getElementById) return do*****ent.getElementById(layerName)
if(do*****ent.all) return do*****ent.all[layerName]
if(do*****ent.layers){
if(parentLayerName){
return findLayer(layerName, eval(parentLayerName))
}else{
return findLayer(layerName, do*****ent.layers)
}
}
}
Теперь мы можем обратиться к нашему слою "cool" так: layer("cool","mylayer") или вообще layer("cool"), но последний вариант будет более "тормозным", т. к. компьютеру придется обходить всё дерево слоев до искомого. Похожее соображение приводит к тому, что логично единожды вызвать слой - var mylayer=layer("xxx"), а потом использовать переменную mylayer для дальнейшей работы со слоем.
С картинками, вставленными в слой, в Netscape 4 дело обстоит так же. Картинки в слое не входят в коллекцию do*****ent.images[] корневого документа, они принадлежат коллекции do*****ent.images[] этого слоя. Пример - у нас есть слой "layer", в нем есть картинка "image". Чтоб поменять у этой картинки src, пишем:
do*****ent.layers["layer"].do*****ent.images["image"].src="file.jpg"
Доступ к CSS-свойствам слоя
Доступ к CSS-свойствам слоя (расположение, видимость и т.д.) также по-разному осуществляется в разных браузерах. В MSIE 4 и DOM1-браузерах доступ к свойству осуществляется через объект .style. Пример (используется уже определенная нами функция вызова слоя):
// спрятать слой в MSIE4 и DOM1-браузерах
layer("mylayer").style.visibility="hidden"
В Netscape 4 у слоя нет поля style, доступ к свойствам осуществляется непосредственно:
// спрятать слой в Netscape 4
layer("mylayer").visibility="hide"
Можно заметить, что даже значение, которое нужно присвоить свойству .visibility, разное у разных браузеров. Хотя более новые версии 4-ого Netscape поддерживают не только "show"/"hide", но и "visible"/"hidden", как и MSIE.
Для доступа к стилям можно порекомендовать такую функцию:
function layerStyle(layerObject){
if(layerObject.style) return layerObject.style //доступ через style
return layerObject //доступ без style
}
Вместе с которой наш пример сократится до layerStyle(layer("mylayer")).visibility=isNetscape4?"hide":"hidden"
Надо, правда, отметить, что приведенный мной пример выглядит несколько громоздким. Это связано с тем, что он, не обладая большой практической пользой, призван лишь проиллюстрировать работу со слоями в разных браузерах. Но вы можете познакомиться с более аккуратно написанной мною же объектно-ориентированной библиотекой DHTML-функций KLayers.
Различия в CSS-свойствах
Хочу повторить, что доступ к CSS-свойствам по-разному осуществляется в разных браузерах.
Видимость слоя (visibility)
- MSIE, Opera, Mozilla - .visibility="visible"/"hidden" (видимый/спрятанный)
- Netscape 4 - .visibility="show"/"hide"
Цвет фона слоя
- MSIE, Mozilla, Opera 6 - .backgroundColor="ЦВЕТ" (напр. "red","#ffee15")
- Netscape 4 - .bgColor="ЦВЕТ"
- Opera 5 - .background="ЦВЕТ" (работает только в том случае, если изначально через CSS для слоя был указан какой-либо фоновый цвет)
Фоновое изображение у слоя
- MSIE, Mozilla, Opera 6 - .backgroundImage="url(url картинки)"
- Netscape 4 - .background.src="url картинки"
Обрезка слоя (clip)
Позволяет сделать только часть слоя видимой. Может применяться для эффектов "распахивания", "выползания" или "скроллинга".
- MSIE, Mozilla - .clip="rect(top,right,bottom,left)"
- Netscape 4 - .clip.top="top", .clip.right="right" и т.д. (top, right, bottom, left - размеры в пикселах, т.е., к примеру, 120px)
- Opera - не поддерживается
Для реализации прокручиваемого текста в новых браузерах (MSIE, Mozilla, Opera) удобно применять css-свойство overflow: hidden. Можно создать блок с overflow: hidden и фиксированными размерами, а внутрь его вложить другой блок, который и будем прокручивать. Для прокрутки достаточно менять ему .style.left и top (или .style.pixelLeft, pixelTop в Opera). В Netscape 4, само собой разумеется, для прокрутки слоя придется пользоваться свойством clip.
Не CSS-свойства
У слоев есть свойства, которые не определяются CSS. Это, к примеру, получившиеся габариты слоя, которые зависят от количества текста, помещенного в нем. Обращаться к этим свойствам надо минуя .style, т. е. просто layer.свойство.
Текущие координаты верхнего левого угла слоя на странице
- MSIE, Opera, Mozilla - .offsetLeft, offsetTop (только для чтения)
- Netscape 4 - .pageX, pageY (можно изменять, двигая слой абсолютно, т. е. относительно окна, а не родительских элементов, если такие есть)
Пример:
// Y-координата верха слоя
function getLayerTop(layer){
if(isMSIE || isOpera5 || isMozilla){
return layer.offsetTop
}else if(isNetscape4){
return layer.pageY
}
}
В DOM1-браузерах (MSIE, Opera, Mozilla) в случае вложенных слоев, т. е. когда слой вложен в другой слой, координаты .offsetLeft и .offsetTop отсчитываются относительно родительского слоя. Для получения ссылки на родительский элемент существует свойство .offsetParent. Можно пройтись по цепочке offsetParentов, суммируя их координаты, пока не дойдем до самого верхнего родителя - do*****ent.body.
Текущие размеры содержимого слоя
- MSIE, Mozilla - .offsetWidth, offsetHeight
- Netscape 4 - .do*****ent.width, height
- Opera - .style.pixelWidth, pixelHeight
Изменение содержимого слояЗапись в слой
- MSIE, Mozilla - .innerHTML=текст
- Netscape 4 - .do*****ent.open()
.do*****ent.write(текст)
.do*****ent.close()
- Opera - невозможно
В MSIE 4 не следует вызывать .innerHTML до наступления onload страницы.
В Netscape 4 есть глюк с записью русских букв в слой. Они превращаются либо в символы кодировки western, либо в знаки вопроса ("?????"). Решение этой проблемы может быть достигнуто через использование загрузки в слой другого документа (см. далее). В слой загружается документ с корректно выставленным charset, а потом в него печатается через do*****ent.write нужный текст.
Подгрузка другого документа в слой
В Netscape 4 слои имеют атрибут и свойство src, а также метод .load(url). Это позволяет записывать в слой содержимое любого документа.
В MSIE и Mozilla вместо этого есть тег
|