Начало · Справочники · Курсы · Разговоры

leechy.ru · Сайт почти придуман

Где находятся элементы?

Когда-то давно я с неудовольствием прочитал у Скота Эндрю ЛеПера (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');

Вот и все.