|
Когда-то давно я с неудовольствием прочитал у Скота Эндрю ЛеПера (scottandrew.com, который достаточно давно занимается dHTML-ем и пишет очень хорошие статьи), что в четвертых браузерах нельзя определить местоположение элемента на странице и поэтому очень часто абсолютно позиционированные слои показываются неадекватно на страницах. Я не поверил. К тому времени я уже знал и использовал трюк с определением координат элементов в NN4, но из-за малого опыта работы с MSIE4 у меня с ним не это получалось. После прочтенного у Скота я сразу-же написал куча сообщений в разных форумов с вопросом "правда ли это?" и на всех ответил только один парень - Джо Хьюит (joehewitt.com, который сейчас работает над NN6!). Решение проблемы, который он тогда предложил работало в IE4+. После небольшого изменения те-же функции заработали и в NN6. Именно о них сегодня пойдет речь. (Зачем я написал эту предисторию? Не люблю присваивать чужие заслуги ;-)
Легче не бывает или прыгающие элементы
NN4 очень часто ругают. Даже я иногда выхожу из себя из-за того, что этот "придурок" не хочет воспринимать то, что ему говорять и чаще всего глючить со страшной силой при любых вложенных элементов... особенно таблиц. Всем известно, что его объектная модель очень бедна, но нужно признать, что то что у него есть - поистине удобно для использования.
Одно из таких вещей - определение местоположения слоя. Именно слоя, посколько NN4 не воспринимает других элементов "на должном уровне". Ну, а чтобы выяснить местоположение какого-то элемента на странице, то нужно просто его сделать относительно спозиционированный слой со смещением top: 0 и left: 0. После этого запросить расположение этого элемента проще простого:
 |
elmLeft = document.layers[id_элемента].pageX;
elmTop = document.layers[id_элемента].pageY; |
 |
То что следует учесть: при использовании относительно позиционированных элементов на больших и "тяжелых" страницах может привести к эффекту "прыгающих элементов" как например у меня получилось при верстке фирменных салонов Nokia. Этот эффект вызван тем, что даже относительные элементы являются для NN4 совершенно отдельный HTML-поток и до полной загрузки страницы он их показывает без учета того, что они вложены в другом элементе. В этом нет ничего страшного, только есть некоторые люди, которым это не нравиться.
Наверх... к корню!
В MSIE, к сожелению, нет переменных типа pageX/pageY. Единственные переменные, которые возвращают более-менее корректную информацию о положении элемента это offsetLeft/offsetTop. Именно их и "приняли" в последствии в DOM level 1. Моя проблема была в том, что они возвращают растояние не до начала координат страницы, а всего-лишь до начала родительского элемента... и к тому-же в то-время я не знал как сделать цикл, который бегал бы наверх по дереве элементов и правильно зоканчичивал свою работу достигнув его начало.
Дело в том, что в MSIE у каждого элемента есть переменная offsetParent, которая указывает на родительский элемент. В принципе, достигнув начала документа offsetParent должен быть равен null. Однако это так не всегда. Если честно, не помню в каких именно случаев это происходило, но Joe Hewitt дал совет обрубать цикл еще, если имя тэга родительского элемента равен "BODY". Что самое интересное - это сработало!
В итоге цикл получился следующим (для определения растояния слева):
 |
var elem = document.all(which);
var pos = elem.offsetLeft;
while (elem.offsetParent != null) {
elem = elem.offsetParent;
pos += elem.offsetLeft;
if (elem.tagName == 'BODY') break;
}
return pos; |
 |
Все изменения, которые нужно было сделать для NN6 на практике оказались всего-лишь изменениями способа доступа к элементам - не через семейство all, а с помощью функции getElementById(). Теперь первая строчка цикла выглядит вот так:
 |
var elem = (dom)? document.getElementById(which) : document.all[which]; |
 |
В итоге, если задуматься, то это действительно правильный способ определения положения элемента, но все-равно мне больше по душе параметры pageX/pageY из NN4.
Одна функция или две?
В зависимости от необходимости измерения того или иного (или обоих) расстояния возможен разный подход к функциям. Если интересны обе координаты, то лучше всего воспользоваться этой функции:
 |
function relPos(which) {
if (nn4) {
this.x = document.layers[which].pageX;
this.y = document.layers[which].pageY;
} else {
var elem = (dom)? document.getElementById(which) : document.all[which];
this.x = elem.offsetLeft;
this.y = elem.offsetTop;
while (elem.offsetParent != null) {
elem = elem.offsetParent;
this.x += elem.offsetLeft;
this.y += elem.offsetTop;
if (elem.tagName == 'BODY') break;
}
}
return this;
} |
 |
Она возвращает объект с двумя переменными внутри него - x и y (соответственно координат) и ее нужно использовать с помощью оператора new:
 |
var elemPos = new relPos('myLeer'); |
 |
После чего координаты будут доступны через elemPos.x и elemPos.y.
Второй способ - сделать две отдельные функции, каждая из которых возвращает число:
 |
function relPosX(which) {
if (nn4) {
return document.layers[which].pageX;
} else {
var elem = (dom)? document.getElementById(which) : document.all[which];
var pos = elem.offsetLeft;
while (elem.offsetParent != null) {
elem = elem.offsetParent;
pos += elem.offsetLeft;
if (elem.tagName == 'BODY') break;
} return pos;
}
}
function relPosY(which) {
if (nn4) {
return document.layers[which].pageY;
} else {
var elem = (dom)? document.getElementById(which) : document.all[which];
var pos = elem.offsetTop;
while (elem.offsetParent != null) {
elem = elem.offsetParent;
pos += elem.offsetTop;
if (elem.tagName == 'BODY') break;
} return pos;
}
} |
 |
Их можно использовать напрямую, например, если интересует x-координата элемента myLeer, то можно просто прописать:
 |
var Xcoord = relPosX('myLeer'); |
 |
Вот и все, если возникли вопросы, пишите по адресу leechy@dhtml.ru
| Москва, 11 февраля 2001. |
Leechy |
|