JS: offsetTop, find position
Возникла потребность определения верхней границы какого-либо объекта. Изучаю. Если не брать в расчёт NN4, то всё сводится к:
function findPosY(obj)
{
var curtop = 0;
while (obj.offsetParent)
{
curtop += obj.offsetTop;
obj = obj.offsetParent;
}
return curtop;
}
Т.е. для каждого объекта, границу которого мы хотим узнать, приходится идти по дереву и суммировать значение offsetTop. Причём (ну кто бы мог подумать, а?) у каждого браузера своё понятие о том, как именно это должно считаться. Помимо перечисленных на quirksmode.org багов браузеров, столкнулся ещё с двумя фишками:
1) В Опере нужно осторожно подходить к вопросу margin’ов, т.е. при расчёте div из кода с такой структурой
<body>
<div style="margin-top:50px">
blah-blah-blah
margin-top учитываться не будет. Насколько я помню, с точки зрения стандартов это не является багом, а даже наоборот, но от этого не легче, потому как остальные браузеры учитывают значение этого отступа. Если вёрстка предполагает подобный margin, то лучше немного изменить функцию:
function findPosY(obj)
{
var curtop = 0;
while (obj)
{
curtop += obj.offsetTop;
obj = obj.offsetParent;
}
return curtop;
}
2) При определённых условиях IE начинает проходить по всем родительским по отношению к искомому элементам. Если нужны координаты большого числа элементов и структура кода страницы «страдает вложенностью», то, по всей видимости, такое поведение плохо скажется на быстродействии.
Язык от усталости заплетается; лучше один раз увидеть. Верхняя кнопка иллюстрирует первую фишку, нижняя — вторую. Текст под ними показывает логику обхода дерева (значение offsetTop каких элементов суммировалось при расчёте).
Update: offsetTop vs getBoundingClientRect
Categories: PHP | comments: (8)
Комментарии
1. Noboonaga 17th December 2004 - 19:00
Меш, кажись ты категорией ошибся =)
Mash:
Ну,.. бывает. :)
2. Mix 18th December 2004 - 15:04
для IE
----------------------
obj.getBoundingRect().top+document.body.scrollTop
----------------------
Mash:
Это не наш метод. :)
3. Mix 18th December 2004 - 15:06
для остальных
function getTop(o)
{
var r=o.offsetTop
while(o=o.offsetParent) r+=o.offsetTop
return r
}
4. Mix 19th December 2004 - 14:55
>>Это не наш метод. :)
Это ещё почему?
Я правда забыл там от полученного нужно 2 пикселя отнять.
Mash:
Во-первых, если я правильно понял (раньше не доводилось с этим сталкиваться), getBoundingRect — это что-то из VB(?).
Во-вторых, скрипты стараюсь писать максимально кроссбраузерно, понимая под этим не разделение на if ie [делаем то-то], if moz [делаем другое] и т.д. Иногда даже получается. :)
5. Mix 20th December 2004 - 11:47
>>getBoundingRect — это что-то из VB
Я ошибся, имел ввиду getBoundingClientRect().top
>>Во-вторых, скрипты стараюсь писать максимально >>кроссбраузерно, понимая под этим не разделение
>>на if ie [делаем то-то], if moz [делаем другое] и т.д.
Я тоже, однако победить ослика можно только его же оружием:
function getTop(o)
{
if(o.getBoundingRect)
return o.getBoundingRect().top+document.body.scrollTop-2
var r=o.offsetTop
while(o=o.offsetParent) r+=o.offsetTop
return r
}
Mash:
ок, буду знать.
6. dull.ru 28th December 2004 - 06:00
а что, для этой категории нет отдельного RSS канала? я бы почитал…
Mash:
Увы. До переделки движка всё никак руки не дойдут.
7. М-си 6th January 2005 - 01:29
возможно, http://pixel-apes.com/tiny-js/travel solves that issue.
Если уже/ещё не поздно =)
Mash:
do {..} while (z=z.offsetParent). Ну да, оно самое. Здесь речь идёт о том, что при определённых обстоятельствах IE может бегать по всем родительским элементам. Когда таких элементов достаточно много и скорость «обсчёта» играет большую роль, то лучше идти другим путём.
Кстати… Я не со зла, а даже наоборот. ;)
8. deadcat 16th November 2005 - 17:30
Для более сложной верстки правильнее будет так:
function getPosition(obj)
{ var left=0,top=0; while(obj)
{ left+=obj.offsetLeft-obj.scrollLeft;
top+=obj.offsetTop-obj.scrollTop;
if(obj.style.borderTopWidth!='')
top+=parseInt(obj.style.borderTopWidth);
if(obj.style.borderLeftWidth!='')
left+=parseInt(obj.style.borderLeftWidth);
obj=obj.offsetParent; }
return {left:left,top:top};
}
Хотя, наверняка, и это не все что придется корректировать…