JS: offsetTop, find position

17th December 2004 - 01:35

Возникла потребность определения верхней границы какого-либо объекта. Изучаю. Если не брать в расчёт 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};
}

Хотя, наверняка, и это не все что придется корректировать…

Комментарии временно отключены.