Как сделать поиск на странице


Как сделать поиск на странице

К странице...

Update: Более новый материал по этой теме находится по адресу https://learn.javascript.ru/coordinates.

Для кросс-браузерного вычисления координат элемента давно используется суммирование offsetLeft/offsetTop. Список глюков этого подхода такой же длинный, как история его существования.

Эта статья - о том, как вычислять координаты не только кросс-браузерно, но и правильно. Да, и еще - быстро.

Логика этого подхода довольно проста.

Она заключается в том, что браузер позиционирует элементы относительно друг друга, и у каждого элемента есть свой "родитель по позиционированию": offsetParent.

В качестве offsetParent обычно выступает родитель parentNode. Но это не всегда так.

Например, для элемента с position='absolute' родителем по позиционированию является ближайший позиционированный родитель, то есть первый элемент в цепочке вложенности, у которого свойство position - одно из: absolute, relative или fixed(не поддерживается IE<7).

Наиболее подробно это описано в стандарте CSS: containing block details. Этот самый "containing block" - как раз и определяет offsetParent, от которого отсчитывается позиция элемента.

Как браузер находит elem.offsetParent

Двигаемся вверх по цепочке родителей elem, останавливаясь на следующих элементах, которые являются offsetParent:

<body> элемент, у которого position - не static(значение по умолчанию) элементы table,th,td - если elem.position='static'

У элемента <body> никогда нет offsetParent.

В IE 7+/Opera у элементов с elem.position='fixed' нет offsetParent.

Сдвиг относительно offsetParent'а задается свойствами offsetTop/offsetLeft:

Остается пройтись по всем offsetParent и просуммировать сдвиги. Последним offsetParent обычно является body:

function getOffsetSum(elem) { var top=0, left=0 while(elem) { top = top + parseFloat(elem.offsetTop) left = left + parseFloat(elem.offsetLeft) elem = elem.offsetParent } return {top: Math.round(top), left: Math.round(left)} }

Основных проблем с этим кодом две.

Он слегка глючит, в разных браузерах - по-разному. Есть проблемы с border'ами элементов, ошибки при прокрутке внутри элементов и некоторые другие. Он медленный. Каждый раз приходится пройти всю цепочку offsetParent'ов.

Вместо того, чтобы писать длинный кроссбраузерный код с разбором багов, который уж точно везде работает корректно, рассмотрим альтернативное решение, которое мало того что соответствует стандарту - его отлично поддерживают Internet Explorer 6+, Firefox 3+ и Opera 9.62+.

Этот малоизвестный метод по стандарту должен быть у каждого элемента DOM.

На момент написания статьи он реализован в SVN у Webkit. Для нас это значит, что Chrome и Safari с его поддержкой будут готовы уже скоро.

Он возвращает прямоугольник, ограничивающий элемент.

Важно, что координаты прямоугольника заданы относительно окна, а не документа, то есть не учитывают прокрутку страницы. Кроме того, координаты прямоугольника могут быть дробными в Firefox 3.

Координаты элемента на странице - это левый-верхний угол прямоугольника + прокрутка страницы.

Код обработчика onclick:

var br=this.getBoundingClientRect() alert("Top:"+br.top+", Left:"+br.left+", Right:"+br.right+", Bottom:"+br.bottom) Как работает getBoundingClientRect() ?

По стандарту CSS любое содержимое находится внутри некоторого прямоугольника: css box.

В случае с блочными элементами, например DIV - этим прямоугольником яляется сам элемент. Такой прямоугольник называют block box.
Если элемент строчный, например, длинный текст - он уже может быть не такой простой формы, а требует для отображения нескольких прямоугольников anonymous box. Обо всем этом подробно об этом написано в стандарте: http://www.w3.org/TR/CSS21/visuren.html#anonymous-block-level".

Так что содержание элемента DOM может находится как в одном, так и в нескольких прямоугольниках css box.

Можно получить список всех прямоугольников, соответствующих elem, вызовом elem.getClientRects(). Здесь IE<8 может сделать свои прямоугольники, не соответствующие стандарту, но они для нас не играют роли, так как метод getClientRects() нам не нужен.

Метод elem.getBoundingClientRect() возвращает один (минимальный) прямоугольник, который включает в себя все прямоугольники getClientRects() с содержимым элемента.

На основе метода elem.getBoundingClientRect() мы можем сделать новый вариант функции:

function getOffsetRect(elem) { // (1) var box = elem.getBoundingClientRect() // (2) var body = document.body var docElem = document.documentElement // (3) var scrollTop = window.pageYOffset || docElem.scrollTop || body.scrollTop var scrollLeft = window.pageXOffset || docElem.scrollLeft || body.scrollLeft // (4) var clientTop = docElem.clientTop || body.clientTop || 0 var clientLeft = docElem.clientLeft || body.clientLeft || 0 // (5) var top = box.top + scrollTop - clientTop var left = box.left + scrollLeft - clientLeft return { top: Math.round(top), left: Math.round(left) } } Получить ограничивающий прямоугольник для элемента. Задать две переменных для удобства Вычислить прокрутку документа. Все браузеры, кроме IE, поддерживают pageXOffset/pageYOffset, а в IE, при наличии DOCTYPE прокрутка вычисляется либо на documentElement(<html>), иначе на body - что есть то и берем Документ(html или body) бывает сдвинут относительно окна (IE). Получаем этот сдвиг. Прибавляем к координатам относительно окна прокрутку и вычитаем сдвиг html/body, чтобы получить координаты относительно документа

Для Firefox дополнительно мы округляем координаты вызовом Math.round().

На демо находятся 3 вложенных DIV'а. Все они с border, некоторые с position/margin/padding.

Клик на внутреннем отображает значения getOffsetSum/getOffsetRect, а также показывает координаты курсора на момент клика: event.pageX/pageY.

Координаты выводятся сразу под DIV'ами.

На момент написания статьи демо работает в IE6+,Firefox 3+ и Opera 9.62+.

Сравнить getOffsetSum и getOffsetRect

getOffsetSum:значение getOffsetSum()

getOffsetRect:значение getOffsetRect()

event:координаты клика мышью относительно документа

Обратите внимание: результаты getOffsetSum(elem) и getOffsetRect(elem) не во всех браузерах совпадают.

Чтобы увидеть, какой вариант правильный - кликните на самой верхней-левой точке элемента (на самом верхнем-левом уголке черной рамки).

Тогда в pageX/pageY события появятся реальные значения угла элемента, и вы сможете их сравнить с getOffsetSum/getOffsetRect. Именно для такого сравнения в демо и добавлен вывод event.

Что делать, если метод getBoundingClientRect не поддерживается?

С одной стороны, мы могли бы рассмотреть корректную кросс-браузерную реализацию для FF2/Safari/Chrome/Konqueror. Она включает в себя много кода для обхода браузерных багов при подсчетах, которые нам совсем не интересны.

С другой - FF2 давно умер, а движок Safari/Chrome содержит поддержку getBoundingClientRect в SVN, и значит она скоро будет в релизе.
Думаю, и такие явные аутсайдеры как Konqueror подсуетятся, т.к движок Konqueror - по сути такой же, как и Safari.

Поэтому предлагаю использовать:

function getOffset(elem) { if (elem.getBoundingClientRect) { // "правильный" вариант return getOffsetRect(elem) } else { // пусть работает хоть как-то return getOffsetSum(elem) } }

Скачать полный финальный вариант.

Вы узнали, как вычислять координаты элемента на странице: быстро и точно в IE6+/FF3+/Opera 9.62+, и как-то чтоб работало - в Safari/Chrome/Konqueror.

Скоро и в Safari/Chrome/Konqueror будет поддержка правильного метода, поэтому аутсайдерами останутся лишь совсем редкие и багливые браузеры, и все будет работать ок .


  Текущий раздел


Как сделать поиск на странице

Как сделать поиск на странице

Как сделать поиск на странице

Как сделать поиск на странице

Похожие новости: