Начинающие веб-программисты рано или поздно сталкиваются с тем, что их скрипт, любовно написанный (или позаимствованный) и прекрасно работающий на домашнем компьютере с MSIE 5.5, почему-то не работает у соседа или клиента на его Opera, Mozilla или Netscape.
Цель настоящей статьи - рассмотреть имеющиеся различия в DHTML-объектах и функциях у нынешних браузеров. Предполагается, что читатель уже более-менее знаком с программированием по крайней мере под один из браузеров.
Браузеров и их версий довольно много, но многие из них не различаются между собой пониманием DHTML. Поэтому есть смысл разбить их на несколько групп, названных по наиболее распространенному представителю. Проверять на принадлежность к той или иной группе будем через поддержку испытуемым браузером тех или иных объектов:
isDOM=document.getElementById //DOM1 browser
(MSIE 5+, Netscape 6, Opera 5+)
isMSIE=document.all &&
document.all.item //Microsoft Internet Explorer
4+
isNetscape4=document.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 в документе должен присутствовать тег <body></body>, иначе document.body не будет определено.
В MSIE такой код прекрасно работает:
<form
name=myform>
<input
name=mytext>
</form>
......
myform.mytext.value="hello"
Но в 4-ом Netscape этот код выдает ошибку. Там нельзя так обращаться к формам. Необходимо писать document.myform.mytext.value="hello" - это будет работать и там, и там. Чтобы избежать возможных конфликтов имени формы с другими объектами document лучше писать document.forms.myform.mytext.value или document.forms["myform"].elements["mytext"].value. Последняя запись рекомендуется в случаях, когда имя формы или ее элемента могут содержать недопустимые для имени переменной в JavaScript символы.
В Mozilla лучше не обращаться к элементам страницы (особенно формам) до наступления события onload.
В Netscape у объекта select нет свойства value. Для выбора нужного option пользуйтесь проверкой select.options[n].value и установкой соответствующего select.selectedIndex. Пользоваться select.options[n].selected=true/false не рекомендуется из-за проблем с Opera (см. далее).
В Opera старых версий (напр. 5.10, в отличие от, к примеру, 5.12) нельзя выбрать опцию select через select.options[n].selected=true. Вместо этого нужно писать select.selectedIndex=n.
Доступ с картинкам, т. е. объектам, создаваемым тегом <img>, осуществляется через коллекцию document./images/[]. Но у Netscape 4 есть особенность в вызове картинки, если она вставлена в слой (см. ниже).
Если в MSIE 4+ и Mozilla (он же Netscape 6) слоем может быть любой элемент страницы, то в Netscape 4 и Opera 5 это обычно контейнер <div></div> с определенным стилями absolute или relative расположением. Некоторые рекомендуют использовать в Netscape 4 тег <layer>, т. к. он лучше понимается Нетскейпом. Поэтому те случаи, когда <div> и <layer> в 4-ом Netscape ведут себя по-разному, возможно, я рассмотрю в одной из будущих статей. Но эти случаи достаточно редки и специфичны и связаны с глюками в форматировании содержимого слоев.
В Netscape 4 не используйте в именах классов (class=) и идентификаторов (id=) символ подчеркивания "_", в противном случае Netscape не увидит этого элемента.
Доступ к слоям по-разному осуществляется в разных браузерах. А именно:
Можно порекомендовать такую функцию:
function
layer(layerName){
//DOM1
if(document.getElementById) return
document.getElementById(layerName)
//MSIE4
if(document.all)
return document.all[layerName]
//Netscape
4
if(document.layers) return
document.layers[layerName]
//неподдерживаемый браузер
return
null
}
С доступом к слоям в Netscape 4 имеется один аспект. От связан с вложенными (nested) слоями, т. е. со слоями, которые описаны внутри контейнеров другого слоя. Пример (предполагается, что в CSS для тегов <div> задано допустимое свойство position):
<div id="mylayer"><div
id="cool">xxx</div></div>
Если в MSIE 4+ можно вызвать слой "cool" через document.all["cool"], то в Netscape 4 document.layers["cool"] вернет undefined. Для вызова вложенного слоя придется написать такую конструкцию:
document.layers["mylayer"].document.layers["cool"]
То же самое касается и адресации других объектов, находящихся внутри слоя - картинок, форм, ссылок и т.д.
Стало быть, нашу функцию можно модернизировать таким образом:
//рекурсивный поиск по слоям
function
findLayer(what, where){
if(!where) return
var i,l,parent
var
len=where.length
for(i=0;i<len;i++){
parent=where[i].document.layers
l=parent[what]
if(l)
return l
l=findLayer(what, parent)
}
return false
}
function
layer(layerName, parentLayerName){
if(document.getElementById) return
document.getElementById(layerName)
if(document.all) return
document.all[layerName]
if(document.layers){
if(parentLayerName){
return
findLayer(layerName, eval(parentLayerName))
}else{
return
findLayer(layerName, document.layers)
}
}
}
Теперь мы можем обратиться к нашему слою "cool" так: layer("cool","mylayer") или вообще layer("cool"), но последний вариант будет более "тормозным", т. к. компьютеру придется обходить всё дерево слоев до искомого. Похожее соображение приводит к тому, что логично единожды вызвать слой - var mylayer=layer("xxx"), а потом использовать переменную mylayer для дальнейшей работы со слоем.
С картинками, вставленными в слой, в Netscape 4 дело обстоит так же. Картинки в слое не входят в коллекцию document./images/[] корневого документа, они принадлежат коллекции document./images/[] этого слоя. Пример - у нас есть слой "layer", в нем есть картинка "image". Чтоб поменять у этой картинки src, пишем:
document.layers["layer"].document./images/["image"].src="file.jpg"
Доступ к 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-свойствам по-разному осуществляется в разных браузерах.
Позволяет сделать только часть слоя видимой. Может применяться для эффектов "распахивания", "выползания" или "скроллинга".
Для реализации прокручиваемого текста в новых браузерах (MSIE, Mozilla, Opera) удобно применять css-свойство overflow: hidden. Можно создать блок с overflow: hidden и фиксированными размерами, а внутрь его вложить другой блок, который и будем прокручивать. Для прокрутки достаточно менять ему .style.left и top (или .style.pixelLeft, pixelTop в Opera). В Netscape 4, само собой разумеется, для прокрутки слоя придется пользоваться свойством clip.
У слоев есть свойства, которые не определяются CSS. Это, к примеру, получившиеся габариты слоя, которые зависят от количества текста, помещенного в нем. Обращаться к этим свойствам надо минуя .style, т. е. просто layer.свойство.
Пример:
// 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ов, суммируя их координаты, пока не дойдем до самого верхнего родителя - document.body.
.document.open()
.document.write(текст)
.document.close()
В MSIE 4 не следует вызывать .innerHTML до наступления onload страницы.
В Netscape 4 есть глюк с записью русских букв в слой. Они превращаются либо в символы кодировки western, либо в знаки вопроса ("?????"). Решение этой проблемы может быть достигнуто через использование загрузки в слой другого документа (см. далее). В слой загружается документ с корректно выставленным charset, а потом в него печатается через document.write нужный текст.
В Netscape 4 слои имеют атрибут и свойство src, а также метод .load(url). Это позволяет записывать в слой содержимое любого документа.
В MSIE и Mozilla вместо этого есть тег <iframe>, который позволяет достичь похожих результатов. Однако целью данной статьи не является подробное рассмотрение этого решения. Скажу лишь, что этот <iframe> делается невидимым с помощью CSS, в него подгружается искомый документ и, после его загрузки, содержимое <iframe> записывается в нужный слой через .innerHTML.
В Opera будут проблемы, т. к. в ней нельзя ни загружать ничего в слой, ни писать в него.
<!--См. подробнее про все это в моей будущей статье про хитрости в борьбе с браузерами (если напишу).
-->В Netscape 4 будет ошибкой создавать слои (или просто теги со стилевым атрибутом style) таким образом:
document.write("<div id='ddd' style='position:
absolute'>...</div>")
Почему-то Netscape 4 не переваривает указание стиля для печатаемого через document.write тега. Рекомендуется вынести стили во внешний отдельный тег <style>:
document.write("<style> #ddd { position:
absolute } </style>")
document.write("<div
id='ddd'>...</div>")
Также можно воспользоваться указанием на class, а не id тега.
Отслеживание событий мыши, как основного средства управления просмотром веб-страниц, имеет большое значение.
<!--Часто бывает нужно узнать текущие координаты курсора мыши на странице, вне зависимости от того, на каком из элементов документа она находится. Создадим функцию, которая будет при событии перемещения мыши помещать в глобальные переменные mousex и mousey текущие координаты курсора относительно документа.
Вот как это делается в различных браузерах:
//Отлавливать событие
MOUSEMOVE
document.captureEvents(Event.MOUSEMOVE)
document.onmousemove=function(e){
mousex
= e.pageX
mousey = e.pageY
return true
}
document.onmousemove=function(e){
mousex = e.pageX
mousey =
e.pageY
return true
}
document.onmousemove=function(){
mousex=event.clientX
mousey=event.clientY
return
true
}
document.onmousemove=function(){
mousex=event.clientX+document.body.scrollLeft
mousey=event.clientY+document.body.scrollTop
return
true
}
Соединяя функции для разных браузеров воедино, получаем:
mousex =
0
mousey = 0
if(isNetscape4)
document.captureEvents(Event.MOUSEMOVE)
if(isMSIE){
document.onmousemove=function(){
mousex=event.clientX+document.body.scrollLeft
mousey=event.clientY+document.body.scrollTop
return
true
}
}else
if(isOpera){
document.onmousemove=function(){
mousex=event.clientX
mousey=event.clientY
return
true
}
}else if(isNetscape4 ||
isMozilla){
document.onmousemove=function(e){
mousex = e.pageX
mousey =
e.pageY
return true
}
}
Само собой, не забудьте вначале определить браузер.
В Opera событие перемещения мыши будет отлавливаться только тогда, когда оно будет происходить над содержимым документа. т. е. если, к примеру, документ короткий и занимает лишь половину окна браузера, то событие будет отмечаться только на верхней половине окна.
Автор: Сергей Круглов
|
Программирование для чайников.
|