Получить позицию (X, Y) HTML-элемента

853

Я хочу знать, как получить X и Y позицию HTML-элементов, таких как img и div в JavaScript.

  • 113
    Относительно чего?
  • 3
    я использовал эти 7 строк кода, который работает во всех браузерах с расхождениями в ie5,6,7 (я не помню, чтобы у меня был какой-нибудь пробоем ... может быть, типа doc) ... quirksmode.org/js/findpos. HTML, и я использовал его много лет. может быть кто-то может указать недостатки, если таковые имеются.
Показать ещё 6 комментариев
Теги:
position
dom

22 ответа

1095

Правильный подход заключается в использовании element.getBoundingClientRect():

var rect = element.getBoundingClientRect();
console.log(rect.top, rect.right, rect.bottom, rect.left);

Internet Explorer поддерживает это, так как до тех пор, пока вы, вероятно, будете заботиться о нем, и он, наконец, был стандартизован в Представлениях CSSOM. Все остальные браузеры уже давно приняли его .

Некоторые браузеры также возвращают свойства высоты и ширины, хотя это нестандартно. Если вы обеспокоены более старой совместимостью с браузером, проверьте эти версии ответов на оптимизированную унизительную реализацию.

Значения, возвращаемые element.getBoundingClientRect(), относятся к окну просмотра. Если вам это нужно относительно другого элемента, просто вычтите один прямоугольник из другого:

var bodyRect = document.body.getBoundingClientRect(),
    elemRect = element.getBoundingClientRect(),
    offset   = elemRect.top - bodyRect.top;

alert('Element is ' + offset + ' vertical pixels from <body>');
  • 105
    Очень интересная статья о координатах JS . Эта статья нигде не упоминается на SO, она должна ..
  • 5
    Не работает ... это 8 пикселей по вертикали и горизонтали, из-за поля в 8px в теле. Решение Меоу отлично работает. Если хотите, у меня есть небольшой тест, чтобы продемонстрировать проблему.
Показать ещё 14 комментариев
227

Библиотеки идут до некоторой длины, чтобы получить точные смещения для элемента.
здесь простая функция, которая выполняет работу в любых обстоятельствах, которые я пробовал.

function getOffset( el ) {
    var _x = 0;
    var _y = 0;
    while( el && !isNaN( el.offsetLeft ) && !isNaN( el.offsetTop ) ) {
        _x += el.offsetLeft - el.scrollLeft;
        _y += el.offsetTop - el.scrollTop;
        el = el.offsetParent;
    }
    return { top: _y, left: _x };
}
var x = getOffset( document.getElementById('yourElId') ).left; 
  • 0
    Кажется, не работает с вложенными друг в друга фреймами.
  • 14
    изменить: el = el.parentNode на: el = el.offsetParent; и, похоже, теперь работает для вложенных фреймов ... Я думаю, это то, что вы хотели?
Показать ещё 11 комментариев
107

Это сработало для меня (изменено с самого высокого проголосовавшего ответа):

function getOffset(el) {
  el = el.getBoundingClientRect();
  return {
    left: el.left + window.scrollX,
    top: el.top + window.scrollY
  }
}

Используя это, мы можем назвать

getOffset(element).left

или

getOffset(element).top
  • 1
    Только это решение работает для меня, когда элемент помещается в div , центрированный с помощью css top:50%, left:50%; transform:translate(-50%, -50%); ... отлично. Согласитесь с вопросом @ScottBiggs. Логично стремиться к простоте.
  • 3
    может быть, это не победитель, потому что он такой же, как решение Энди Э, но не работает в старых браузерах <IE9
Показать ещё 8 комментариев
27

Вы можете добавить два свойства в Element.prototype, чтобы получить верхнюю/левую часть любого элемента.

Object.defineProperty( Element.prototype, 'documentOffsetTop', {
    get: function () { 
        return this.offsetTop + ( this.offsetParent ? this.offsetParent.documentOffsetTop : 0 );
    }
} );

Object.defineProperty( Element.prototype, 'documentOffsetLeft', {
    get: function () { 
        return this.offsetLeft + ( this.offsetParent ? this.offsetParent.documentOffsetLeft : 0 );
    }
} );

Это называется так:

var x = document.getElementById( 'myDiv' ).documentOffsetLeft;

Здесь демонстрационное сравнение результатов с jQuery offset().top и .left: http://jsfiddle.net/ThinkingStiff/3G7EZ/

  • 11
    изменение Element.prototype обычно считается плохой идеей . Это приводит к очень сложному обслуживанию кода. Также этот код не учитывает прокрутку.
27

Если страница включает в себя - по крайней мере, любую "DIV", функция, заданная meouw, выдает значение "Y" за пределы текущей страницы. Чтобы найти точную позицию, вам нужно обрабатывать как "offsetParent", так и "parentNode".

Попробуйте приведенный ниже код (он проверяется на FF2):


var getAbsPosition = function(el){
    var el2 = el;
    var curtop = 0;
    var curleft = 0;
    if (document.getElementById || document.all) {
        do  {
            curleft += el.offsetLeft-el.scrollLeft;
            curtop += el.offsetTop-el.scrollTop;
            el = el.offsetParent;
            el2 = el2.parentNode;
            while (el2 != el) {
                curleft -= el2.scrollLeft;
                curtop -= el2.scrollTop;
                el2 = el2.parentNode;
            }
        } while (el.offsetParent);

    } else if (document.layers) {
        curtop += el.y;
        curleft += el.x;
    }
    return [curtop, curleft];
};

  • 1
    как и в других решениях даже с FF 24.4, вышеприведенный код не работает, когда ширины границ существуют как часть макета позиционирования.
  • 0
    Вы спасатель. Я работаю над некоторыми довольно глубокими ошибками GWT прямо сейчас, и добавление этого в метод JSNI решает все мои проблемы !!
20

Чтобы получить позицию относительно страницы эффективно и без использования рекурсивной функции: (также включает IE)

var element = document.getElementById('elementId'); //replace elementId with your element Id.
var rect = element.getBoundingClientRect();
var elementLeft,elementTop; //x and y
var scrollTop = document.documentElement.scrollTop?
                document.documentElement.scrollTop:document.body.scrollTop;
var scrollLeft = document.documentElement.scrollLeft?                   
                 document.documentElement.scrollLeft:document.body.scrollLeft;
elementTop = rect.top+scrollTop;
elementLeft = rect.left+scrollLeft;
  • 0
    Включение всех важных scrollTop / scrollLeft делает этот ответ немного более правильным.
18

Элементы HTML в большинстве браузеров будут иметь: -

offsetLeft
offsetTop

Они определяют положение элемента относительно его ближайшего родителя, у которого есть макет. К этому родительскому объекту часто можно обращаться через свойство offsetParent.

IE и FF3 имеют

clientLeft
clientTop

Эти свойства менее распространены, они определяют позицию элементов с клиентской областью его родителей (заполненная область является частью клиентской области, но граница и маржа не являются).

15

Вам может быть лучше, если использовать фреймворк JavaScript, который имеет функции для возврата такой информации (и многое другое!) независимо от браузера. Вот несколько:

В этих рамках вы можете сделать что-то вроде: $('id-of-img').top для получения координаты y-пикселя изображения.

  • 0
    Вы знаете, как отличаются реализации? Библиотеки, использующие функции, очень похожи на перечисленные выше?
  • 2
    @Costa Все они используют эти виды функций, хотя это развивающееся поле, поскольку различные браузеры по-разному соответствуют спецификации. Большая часть кода посвящена исправлению ошибок. Вы действительно должны смотреть на исходный код.
Показать ещё 8 комментариев
13

jQuery . offset() получит текущие координаты первого элемента или задает координаты каждого элемента в наборе согласованных элементов относительно документа.

  • 0
    По некоторым причинам он не дает такие же результаты в IE по сравнению с другими браузерами. Я думаю, что в IE это дает положение относительно окна, поэтому, если вы прокрутите, вы получите другие результаты в IE по сравнению с другими
  • 1
    offset () сбивает с толку масштабирование, по крайней мере, в Android Chrome, поэтому оно бесполезно. stackoverflow.com/a/11396681/1691517 показывает толерантный к масштабированию способ.
11

Как о чем-то подобном, передавая идентификатор элемента, и он вернет левый или верхний, мы можем их объединить:

1) найти слева

function findLeft(element) {
  var rec = document.getElementById(element).getBoundingClientRect();
  return rec.left + window.scrollX;
} //call it like findLeft('#header');

2) найдите верхний

function findTop(element) {
  var rec = document.getElementById(element).getBoundingClientRect();
  return rec.top + window.scrollY;
} //call it like findTop('#header');

или 3) найдите слева и сверху вместе

function findTopLeft(element) {
  var rec = document.getElementById(element).getBoundingClientRect();
  return {top: rec.top + window.scrollY, left: rec.left + window.scrollX};
} //call it like findTopLeft('#header');
7

Я принял @meouw ответ, добавленный в clientLeft, который позволяет использовать границу, а затем создал три версии:

getAbsoluteOffsetFromBody - аналогично @meouw, это получает абсолютную позицию относительно элемента body или html документа (в зависимости от режима quirks)

getAbsoluteOffsetFromGivenElement - возвращает абсолютную позицию относительно данного элемента (relativeEl). Обратите внимание, что данный элемент должен содержать элемент el, иначе он будет вести себя так же, как getAbsoluteOffsetFromBody. Это полезно, если у вас есть два элемента, содержащиеся в другом (известном) элементе (необязательно несколько узлов вверх по дереву node) и хотите сделать их одинаковыми.

getAbsoluteOffsetFromRelative - возвращает абсолютную позицию относительно первого родительского элемента с положением: relative. Это похоже на getAbsoluteOffsetFromGivenElement по той же причине, но будет только до первого совпадающего элемента.

getAbsoluteOffsetFromBody = function( el )
{   // finds the offset of el from the body or html element
    var _x = 0;
    var _y = 0;
    while( el && !isNaN( el.offsetLeft ) && !isNaN( el.offsetTop ) )
    {
        _x += el.offsetLeft - el.scrollLeft + el.clientLeft;
        _y += el.offsetTop - el.scrollTop + el.clientTop;
        el = el.offsetParent;
    }
    return { top: _y, left: _x };
}

getAbsoluteOffsetFromGivenElement = function( el, relativeEl )
{   // finds the offset of el from relativeEl
    var _x = 0;
    var _y = 0;
    while( el && el != relativeEl && !isNaN( el.offsetLeft ) && !isNaN( el.offsetTop ) )
    {
        _x += el.offsetLeft - el.scrollLeft + el.clientLeft;
        _y += el.offsetTop - el.scrollTop + el.clientTop;
        el = el.offsetParent;
    }
    return { top: _y, left: _x };
}

getAbsoluteOffsetFromRelative = function( el )
{   // finds the offset of el from the first parent with position: relative
    var _x = 0;
    var _y = 0;
    while( el && !isNaN( el.offsetLeft ) && !isNaN( el.offsetTop ) )
    {
        _x += el.offsetLeft - el.scrollLeft + el.clientLeft;
        _y += el.offsetTop - el.scrollTop + el.clientTop;
        el = el.offsetParent;
        if (el != null)
        {
            if (getComputedStyle !== 'undefined')
                valString = getComputedStyle(el, null).getPropertyValue('position');
            else
                valString = el.currentStyle['position'];
            if (valString === "relative")
                el = null;
        }
    }
    return { top: _y, left: _x };
}

Если у вас все еще есть проблемы, особенно связанные с прокруткой, вы можете попробовать http://www.greywyvern.com/?post=331 - я заметил по крайней мере один кусок сомнительного кода в getStyle, который должно быть хорошо, если браузеры будут вести себя, но не проверили все остальное.

  • 0
    Интересно ... но в Edge getAbsoluteOffsetFromBody (element) .top возвращает другое значение при прокрутке, в Chrome оно остается неизменным при прокрутке. Кажется, Edge корректируется с помощью положения прокрутки. Когда элемент прокручивается из поля зрения, я получаю отрицательное значение.
  • 0
    getAbsoluteOffsetFromRelative сделал мой день, я должен был воспроизвести всплывающую подсказку без Javascript начальной загрузки, это мне очень помогло.
7

если использовать jQuery, размерный плагин отличный и позволяет вам точно указать, что вы хотите.

например.

Относительное положение, абсолютное положение, абсолютное положение без заполнения, с заполнением...

Продолжайте, давайте просто скажем, что вы можете с ним многое сделать.

Кроме того, бонус использования jQuery - это легкий размер файла и простота использования, после этого вы не вернетесь к JavaScript без него.

6

Если вы используете jQuery, это может быть простое решение:

<script>
  var el = $("#element");
  var position = el.position();
  console.log( "left: " + position.left + ", top: " + position.top );
</script>
6

Это лучший код, который мне удалось создать (работает и в iframe, в отличие от jQuery offset()). Кажется, у webkit есть немного другое поведение.

Основываясь на комментариях meouw:

function getOffset( el ) {
    var _x = 0;
    var _y = 0;
    while( el && !isNaN( el.offsetLeft ) && !isNaN( el.offsetTop ) ) {
        _x += el.offsetLeft - el.scrollLeft;
        _y += el.offsetTop - el.scrollTop;
        // chrome/safari
        if ($.browser.webkit) {
            el = el.parentNode;
        } else {
            // firefox/IE
            el = el.offsetParent;
        }
    }
    return { top: _y, left: _x };
}
  • 0
    Обратите внимание, что вам понадобится jQuery для использования $.browser.webkit ; Вам нужно поиграться с navigator.userAgent чтобы сделать то же самое с чистым JavaScript .
  • 1
    $ .browser больше не доступен в последней версии jQuery
Показать ещё 1 комментарий
4

Разница между маленькими и маленькими

function getPosition( el ) {
    var x = 0;
    var y = 0;
    while( el && !isNaN( el.offsetLeft ) && !isNaN( el.offsetTop ) ) {
    x += el.offsetLeft - el.scrollLeft;
    y += el.offsetTop - el.scrollTop;
    el = el.offsetParent;
    }
    return { top: y, left: x };
}

Посмотрите пример координат: http://javascript.info/tutorial/coordinates

  • 0
    К сожалению, это не работает для всех элементов, которые можно найти в современном HTML-документе. Например, встроенные элементы <svg> не имеют offsetLeft , offsetTop и offsetParent .
  • 0
    Это просто DIV или IMG , а не SVG . Посмотреть "Посмотреть пример координат"? Вы можете найти объект svg в позиции вызова getOffsetRect , попробуйте и зависит от работы объекта.
4

Самый чистый подход, который я нашел, - это упрощенная версия метода, используемого jQuery offset. Подобно некоторым другим ответам, он начинается с getBoundingClientRect; он затем использует window и documentElement для настройки положения прокрутки, а также таких вещей, как margin на body (часто по умолчанию).

var rect = el.getBoundingClientRect();
var docEl = document.documentElement;

var rectTop = rect.top + window.pageYOffset - docEl.clientTop;
var rectLeft = rect.left + window.pageXOffset - docEl.clientLeft;

var els = document.getElementsByTagName("div");
var docEl = document.documentElement;

for (var i = 0; i < els.length; i++) {

  var rect = els[i].getBoundingClientRect();

  var rectTop = rect.top + window.pageYOffset - docEl.clientTop;
  var rectLeft = rect.left + window.pageXOffset - docEl.clientLeft;

  els[i].innerHTML = "<b>" + rectLeft + ", " + rectTop + "</b>";
}
div {
  width: 100px;
  height: 100px;
  background-color: red;
  border: 1px solid black;
}
#rel {
  position: relative;
  left: 10px;
  top: 10px;
}
#abs {
  position: absolute;
  top: 250px;
  left: 250px;
}
<div id="rel"></div>
<div id="abs"></div>
<div></div>
3

Я сделал это так, чтобы он был перекрестно совместим со старыми браузерами.

// For really old browser or incompatible ones
    function getOffsetSum(elem) {
        var top = 0,
            left = 0,
            bottom = 0,
            right = 0

         var width = elem.offsetWidth;
         var height = elem.offsetHeight;

        while (elem) {
            top += elem.offsetTop;
            left += elem.offsetLeft;
            elem = elem.offsetParent;
        }

         right = left + width;
         bottom = top + height;

        return {
            top: top,
            left: left,
            bottom: bottom,
            right: right,
        }
    }

    function getOffsetRect(elem) {
        var box = elem.getBoundingClientRect();

        var body = document.body;
        var docElem = document.documentElement;

        var scrollTop = window.pageYOffset || docElem.scrollTop || body.scrollTop;
        var scrollLeft = window.pageXOffset || docElem.scrollLeft || body.scrollLeft;

        var clientTop = docElem.clientTop;
        var clientLeft = docElem.clientLeft;


        var top = box.top + scrollTop - clientTop;
        var left = box.left + scrollLeft - clientLeft;
        var bottom = top + (box.bottom - box.top);
        var right = left + (box.right - box.left);

        return {
            top: Math.round(top),
            left: Math.round(left),
            bottom: Math.round(bottom),
            right: Math.round(right),
        }
    }

    function getOffset(elem) {
        if (elem) {
            if (elem.getBoundingClientRect) {
                return getOffsetRect(elem);
            } else { // old browser
                return getOffsetSum(elem);
            }
        } else
            return null;
    }

Подробнее о координатах в JavaScript здесь: http://javascript.info/tutorial/coordinates

2

Я успешно использовал решение Andy E для размещения бутстрапа 2 в зависимости от того, какую ссылку в строке таблицы пользователь нажимает. Страница - это страница Tapestry 5, а javascript ниже импортируется в класс страницы java.

JavaScript:

function setLinkPosition(clientId){
var bodyRect = document.body.getBoundingClientRect(),
elemRect = clientId.getBoundingClientRect(),
offset   = elemRect.top - bodyRect.top;
offset   = offset + 20;
$('#serviceLineModal').css("top", offset);

}

Мой модальный код:

<div id="serviceLineModal" class="modal hide fade add-absolute-position" data-backdrop="static" 
 tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true" style="top:50%;">
<div class="modal-header">
    <button type="button" class="close" data-dismiss="modal" aria-hidden="true">x</button>
    <h3 id="myModalLabel">Modal header</h3>
</div>

<div class="modal-body">
    <t:zone t:id="modalZone" id="modalZone">
        <p>You selected service line number: ${serviceLineNumberSelected}</p>
    </t:zone>
</div>

<div class="modal-footer">
    <button class="btn" data-dismiss="modal" aria-hidden="true">Close</button>
    <!-- <button class="btn btn-primary">Save changes</button> -->
</div>

Ссылка в цикле:

<t:loop source="servicesToDisplay" value="service" encoder="encoder">
<tr style="border-right: 1px solid black;">       
    <td style="white-space:nowrap;" class="add-padding-left-and-right no-border"> 
        <a t:type="eventLink" t:event="serviceLineNumberSelected" t:context="service.serviceLineNumber" 
            t:zone="pageZone" t:clientId="modalLink${service.serviceLineNumber}"
            onmouseover="setLinkPosition(this);">
            <i class="icon-chevron-down"></i> <!-- ${service.serviceLineNumber} -->
        </a>
    </td>

И код Java в классе страницы:

void onServiceLineNumberSelected(String number){
    checkForNullSession();
    serviceLineNumberSelected = number;
    addOpenServiceLineDialogCommand();
    ajaxResponseRenderer.addRender(modalZone);
}

protected void addOpenServiceLineDialogCommand() {
    ajaxResponseRenderer.addCallback(new JavaScriptCallback() {
        @Override
        public void run(JavaScriptSupport javascriptSupport) {
            javascriptSupport.addScript("$('#serviceLineModal').modal('show');");
        }
    });
}

Надеюсь, что это поможет кому-то, этот пост помог.

  • 0
    Этот js-код помог мне, у меня есть старое приложение webforms, использующее множественные заполнители для вставки страниц .aspx, и я не мог понять, как добавить appendChild () из всплывающего окна, которое я создавал, чтобы получить его в окне просмотра, потому что все дерево DOM не работает с несколькими заполнителями и неправильной разметкой. Использование body.getBoundingClientRect (), а затем использование его вершины в позиционировании было тем, что мне было нужно. Благодарю.
2

Просто подумал, что я тоже выброшу это.
Я не смог проверить его в старых браузерах, но он работает в последней версии 3.:)

Element.prototype.getOffsetTop = function() {
    return ( this.parentElement )? this.offsetTop + this.parentElement.getOffsetTop(): this.offsetTop;
};
Element.prototype.getOffsetLeft = function() {
    return ( this.parentElement )? this.offsetLeft + this.parentElement.getOffsetLeft(): this.offsetLeft;
};
Element.prototype.getOffset = function() {
    return {'left':this.getOffsetLeft(),'top':this.getOffsetTop()};
};
1

После долгих исследований и испытаний это, похоже, работает

function getPosition(e) {
    var isNotFirefox = (navigator.userAgent.toLowerCase().indexOf('firefox') == -1);
    var x = 0, y = 0;
    while (e) {
        x += e.offsetLeft - e.scrollLeft + (isNotFirefox ? e.clientLeft : 0);
        y += e.offsetTop - e.scrollTop + (isNotFirefox ? e.clientTop : 0);
        e = e.offsetParent;
    }
    return { x: x + window.scrollX, y: y + window.scrollY };
}

см. http://jsbin.com/xuvovalifo/edit?html,js,output

0

Хотя это, скорее всего, будет потеряно в основе стольких ответов, верхние решения здесь не работают для меня.
Насколько я мог сказать, ни один из других ответов не помог бы.

<Б > Ситуация:
На странице HTML5 у меня было меню, которое было элементом навигации внутри заголовка (не заголовком, а заголовком в другом элементе).
Я хотел, чтобы навигационная система придерживалась вершины, как только пользователь прокручивал ее, но до этого заголовок был абсолютно позиционирован (так что я мог бы немного наложить что-то еще).
Вышеупомянутые решения никогда не приводили к изменению, потому что .offsetTop не собирался меняться, поскольку это был элемент с абсолютным позиционированием. Кроме того, свойство .scrollTop было просто вершиной верхнего элемента..., то есть 0 и всегда было бы 0.
Любые тесты, которые я выполнил, используя эти два (и то же самое с результатами getBoundingClientRect), не сказали бы мне, что верхняя часть навигационной панели когда-либо прокручивалась до верхней части просматриваемой страницы (опять же, как сообщалось на консоли, они просто оставались одинаковыми числами при прокрутке произошло).

Решение
Решение для меня использовало

window.visualViewport.pageTop

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

Возможно, нет необходимости говорить, что в любое время, когда я занимаюсь прокруткой, я ожидаю использовать это решение для программного реагирования на перемещение прокручиваемых элементов.
Надеюсь, это поможет кому-то другому.

  • 0
    это очень вероятно, чтобы быть в верхней части так много ответов, когда вы нажимаете активный, чтобы отсортировать ответы
  • 0
    @lucchi: D хорошо, я полагаю, я мог бы удалить текст @ сверху ... хотя я только что повторил этот вопрос и понял, что нашел лучшее решение для своей проблемы ... хотя мой ответ скорее обратите внимание на более полные ответы. Я подозреваю, что мои требования были не настолько распространены, чтобы другие ответы не работали для меня?
Показать ещё 1 комментарий
0

Поскольку разные браузеры - это визуализация границы, отступов, полей и т.д. по-разному. Я написал небольшую функцию для получения верхней и левой позиций определенного элемента в каждом корневом элементе, который вы хотите в точном размере:

function getTop(root, offset) {
    var rootRect = root.getBoundingClientRect();
    var offsetRect = offset.getBoundingClientRect();
    return offsetRect.top - rootRect.top;
}

Для получения левой позиции вы должны вернуть:

    return offsetRect.left - rootRect.left;

Ещё вопросы

Сообщество Overcoder
Наверх
Меню