Chcę wiedzieć, jak uzyskać pozycję X i Y elementów HTML, takich jak img
i div
JavaScript w stosunku do okna przeglądarki.
Chcę wiedzieć, jak uzyskać pozycję X i Y elementów HTML, takich jak img
i div
JavaScript w stosunku do okna przeglądarki.
Odpowiedzi:
Prawidłowe podejście polega na użyciu element.getBoundingClientRect()
:
var rect = element.getBoundingClientRect();
console.log(rect.top, rect.right, rect.bottom, rect.left);
Internet Explorer obsługuje to od czasu, gdy będziesz się tym przejmować, i został w końcu ujednolicony w widokach CSSOM . Wszystkie inne przeglądarki przyjęły to dawno temu .
Niektóre przeglądarki zwracają również właściwości wysokości i szerokości, choć jest to niestandardowe. Jeśli martwisz się o starszą kompatybilność przeglądarki, sprawdź wersje tej odpowiedzi, aby uzyskać zoptymalizowaną degradującą implementację.
Wartości zwracane przez element.getBoundingClientRect()
są względne do rzutni. Jeśli potrzebujesz go względem innego elementu, po prostu odejmij jeden prostokąt od drugiego:
var bodyRect = document.body.getBoundingClientRect(),
elemRect = element.getBoundingClientRect(),
offset = elemRect.top - bodyRect.top;
alert('Element is ' + offset + ' vertical pixels from <body>');
<body>
.
element.getBoundingClientRect().top
nie zwraca poprawnego górnego przesunięcia w przypadku position: fixed
elementu w iOS (iPhone 8), jeśli zawiera on skoncentrowany element wejściowy, a wirtualna klawiatura przesuwa się w górę. Na przykład, jeśli faktyczne przesunięcie od góry okna wynosi 200 pikseli, zgłosi to jako 420 pikseli, gdzie 220 pikseli to wysokość wirtualnej klawiatury. Jeśli ustawisz element position: absolute
i umieścisz go w tej samej pozycji, jak gdyby był ustalony, otrzymasz poprawną wartość 200 pikseli. Nie znam rozwiązania tego.
Biblioteki dokładają starań, aby uzyskać dokładne przesunięcia elementu.
oto prosta funkcja, która wykonuje zadanie w każdych okolicznościach, których próbowałem.
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;
To zadziałało dla mnie (zmodyfikowane w stosunku do najlepiej głosowanej odpowiedzi):
function getOffset(el) {
const rect = el.getBoundingClientRect();
return {
left: rect.left + window.scrollX,
top: rect.top + window.scrollY
};
}
Za pomocą tego możemy zadzwonić
getOffset(element).left
lub
getOffset(element).top
div
środku, którego użycie css jest top:50%, left:50%; transform:translate(-50%, -50%);
... doskonałe. Zgadzam się z pytaniem @ScottBiggs. Logiczne jest poszukiwanie prostoty.
rect
za pomocą var
, let
lub const
. W przeciwnym razie jest to zdefiniowane jako window.rect
.
left: rect.left + window.scrollX + (rect.width / 2), top: rect.top + window.scrollY + (rect.height / 2)
zwróci środkową pozycję elementu
Jeśli chcesz to zrobić tylko w javascript , oto jeden wkładki wykorzystującegetBoundingClientRect()
window.scrollY + document.querySelector('#elementId').getBoundingClientRect().top // Y
window.scrollX + document.querySelector('#elementId').getBoundingClientRect().left // X
Pierwszy wiersz zwróci wartość offsetTop
Y względem dokumentu. Drugi wiersz zwróci „ offsetLeft
X” względem dokumentu.
getBoundingClientRect()
to funkcja javascript, która zwraca pozycję elementu względem okienka okna.
Elementy HTML w większości przeglądarek będą miały: -
offsetLeft
offsetTop
Określają one pozycję elementu względem najbliższego elementu nadrzędnego, który ma układ. Do tego rodzica często można uzyskać dostęp, jeśli właściwość offsetParent.
IE i FF3 mają
clientLeft
clientTop
Te właściwości są mniej powszechne, określają pozycję elementów z nadrzędnym obszarem klienta (obszar wypełniony jest częścią obszaru klienta, ale nie ma granicy i marginesu).
Jeśli strona zawiera - przynajmniej - „DIV”, funkcja podana przez meouw wyrzuca wartość „Y” poza obecne limity strony. Aby znaleźć dokładną pozycję, musisz obsługiwać zarówno offsetParent, jak i parentNode.
Wypróbuj kod podany poniżej (jest sprawdzany pod kątem 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];
};
Możesz dodać dwie właściwości, aby Element.prototype
uzyskać górę / lewo dowolnego elementu.
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 );
}
} );
To się nazywa tak:
var x = document.getElementById( 'myDiv' ).documentOffsetLeft;
Oto demo porównujące wyniki z jQuery offset().top
i .left
: http://jsfiddle.net/ThinkingStiff/3G7EZ/
Element.prototype
jest ogólnie uważane za zły pomysł . Prowadzi to do naprawdę trudnego do utrzymania kodu. Ponadto ten kod nie uwzględnia przewijania.
Aby efektywnie pobrać pozycję względem strony i bez korzystania z funkcji rekurencyjnej: (obejmuje również IE)
var element = document.getElementById('elementId'); //replace elementId with your element's 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;
Co powiesz na coś takiego, przekazując identyfikator elementu, a zwróci on lewą lub górną część, możemy je również połączyć:
1) znajdź w lewo
function findLeft(element) {
var rec = document.getElementById(element).getBoundingClientRect();
return rec.left + window.scrollX;
} //call it like findLeft('#header');
2) znajdź górę
function findTop(element) {
var rec = document.getElementById(element).getBoundingClientRect();
return rec.top + window.scrollY;
} //call it like findTop('#header');
lub 3) znajdź razem lewą i górną
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');
Możesz być lepiej obsługiwany za pomocą frameworka JavaScript, który ma funkcje zwracania takich informacji (i wiele więcej!) W sposób niezależny od przeglądarki. Tu jest kilka:
Za pomocą tych ram możesz zrobić coś takiego:
$('id-of-img').top
uzyskać współrzędną y-piksela obrazu.
jQuery .offset () pobierze bieżące współrzędne pierwszego elementu lub ustawi współrzędne każdego elementu w zestawie dopasowanych elementów względem dokumentu.
Wziąłem odpowiedź @ meouw, dodałem w clientLeft, która pozwala na obramowanie, a następnie utworzyłem trzy wersje:
getAbsoluteOffsetFromBody - podobnie jak @ meouw, uzyskuje bezwzględną pozycję względem treści lub elementu HTML dokumentu (w zależności od trybu dziwactwa)
getAbsoluteOffsetFromGivenElement - zwraca pozycję bezwzględną względem podanego elementu (relativeEl). Zauważ, że dany element musi zawierać element el, inaczej będzie on zachowywać się tak samo jak getAbsoluteOffsetFromBody. Jest to przydatne, jeśli masz dwa elementy zawarte w innym (znanym) elemencie (opcjonalnie kilka węzłów powyżej drzewa węzłów) i chcesz ustawić je w tej samej pozycji.
getAbsoluteOffsetFromRelative - zwraca pozycję bezwzględną względem pierwszego elementu nadrzędnego z pozycją: względna. Jest to podobne do getAbsoluteOffsetFromGivenElement, z tego samego powodu, ale pójdzie tylko tak daleko, jak pierwszy pasujący element.
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 };
}
Jeśli nadal masz problemy, szczególnie związane z przewijaniem, możesz spróbować spojrzeć na stronę http://www.greywyvern.com/?post=331 - Zauważyłem co najmniej jeden wątpliwy kod w getStyle, który powinien być odpowiedni przy założeniu, że przeglądarki zachowują się , ale w ogóle nie przetestowałem reszty.
jeśli używasz jQuery, wtyczka wymiarów jest doskonała i pozwala ci dokładnie określić, czego chcesz.
na przykład
Pozycja względna, pozycja bezwzględna, pozycja bezwzględna bez wypełnienia, z wypełnieniem ...
To trwa, powiedzmy, że możesz wiele z tym zrobić.
Plus zaletą korzystania z jQuery jest niewielki rozmiar pliku i łatwa obsługa, bez niego nie wrócisz do JavaScript.
To najlepszy kod, jaki udało mi się stworzyć (działa również w ramkach iframe, w przeciwieństwie do offset () jQuery). Wygląda na to, że webkit ma trochę inne zachowanie.
Na podstawie komentarza 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 };
}
Jeśli używasz jQuery, może to być proste rozwiązanie:
<script>
var el = $("#element");
var position = el.position();
console.log( "left: " + position.left + ", top: " + position.top );
</script>
Różnica między małym a małym
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 };
}
Zobacz przykładowe współrzędne: http://javascript.info/tutorial/coordinates
<svg>
elementy, na przykład, nie mam offsetLeft
, offsetTop
i offsetParent
właściwości.
DIV
lub IMG
nie SVG
. Wyświetl „Zobacz przykładowe współrzędne”? możesz znaleźć obiekt svg to wywołanie pozycji getOffsetRect , spróbuj uzależnić pracę obiektu.
Najczystsze podejście, jakie znalazłem, to uproszczona wersja techniki używanej przez jQuery's offset
. Podobnie jak niektóre inne odpowiedzi, zaczyna się od getBoundingClientRect
; następnie używa window
i documentElement
do regulacji pozycji przewijania, a także rzeczy takich jak margines na body
(często domyślny).
var rect = el.getBoundingClientRect();
var docEl = document.documentElement;
var rectTop = rect.top + window.pageYOffset - docEl.clientTop;
var rectLeft = rect.left + window.pageXOffset - docEl.clientLeft;
Chociaż jest bardzo prawdopodobne, że zostanie to utracone u podstaw tak wielu odpowiedzi, najlepsze rozwiązania tutaj nie działały dla mnie.
O ile mogłem powiedzieć, żadna z pozostałych odpowiedzi nie pomogłaby.
Sytuacja :
na stronie HTML5 miałem menu, które było elementem nawigacyjnym w nagłówku (nie w nagłówku, ale w innym elemencie).
Chciałem, aby nawigacja trzymała się góry, gdy użytkownik przewinął ją, ale wcześniej nagłówek był pozycjonowany absolutnie (więc mogłem pozwolić, aby nakładało się na coś innego).
Powyższe rozwiązania nigdy nie wywołały zmiany, ponieważ .offsetTop nie miał się zmienić, ponieważ był to element pozycjonowany bezwzględnie. Dodatkowo właściwość .scrollTop była po prostu najwyższym elementem na najwyższym poziomie ... to znaczy 0 i zawsze wynosiłaby 0.
Wszelkie testy, które przeprowadziłem przy użyciu tych dwóch (i to samo z wynikami getBoundingClientRect) nie powiedziały mi, że góra paska nawigacyjnego kiedykolwiek przewinęła się do góry widocznej strony (ponownie, jak podano w konsoli, po prostu pozostały te same liczby podczas przewijania wystąpił).
Rozwiązanie
Rozwiązanie było dla mnie przydatne
window.visualViewport.pageTop
Wartość właściwości pageTop odzwierciedla widoczną sekcję ekranu, dzięki czemu mogę śledzić, gdzie element znajduje się w odniesieniu do granic widocznego obszaru.
Prawdopodobnie nie trzeba dodawać, że kiedykolwiek zajmuję się przewijaniem, spodziewam się, że użyję tego rozwiązania, aby programowo reagować na ruch przewijanych elementów.
Mam nadzieję, że pomoże to komuś innemu.
WAŻNA UWAGA: Wygląda na to, że działa obecnie w Chrome i Operze i zdecydowanie nie w Firefoksie (6-2018) ... dopóki Firefox nie obsługuje visualViewport, NIE zalecam używania tej metody (i mam nadzieję, że wkrótce to zrobi ... bardziej sensowny niż reszta).
AKTUALIZACJA:
Tylko uwaga dotycząca tego rozwiązania.
Chociaż wciąż uważam, że to, co odkryłem, jest bardzo cenne w sytuacjach, w których „… programowo reaguję na ruch przewijanych elementów”. ma zastosowanie. Lepszym rozwiązaniem problemu, jaki miałem, było użycie CSS do ustawienia pozycji: lepkina elemencie. Używając opcji lepkiej możesz pozostawić element na górze bez użycia javascript (UWAGA: czasami nie będzie to działało tak skutecznie, jak zmiana elementu na naprawioną, ale dla większości zastosowań lepkie podejście będzie prawdopodobnie lepsze)
UPDATE01:
Więc zdałem sobie sprawę, że dla innej strony miałem wymóg, w którym musiałem wykryć pozycję elementu w nieco złożonej konfiguracji przewijania (paralaksa plus elementy przewijające się w ramach wiadomości). W tym scenariuszu zdałem sobie sprawę, że poniższe informacje podały wartość, którą wykorzystałem do ustalenia, kiedy coś zrobić:
let bodyElement = document.getElementsByTagName('body')[0];
let elementToTrack = bodyElement.querySelector('.trackme');
trackedObjPos = elementToTrack.getBoundingClientRect().top;
if(trackedObjPos > 264)
{
bodyElement.style.cssText = '';
}
Mam nadzieję, że ta odpowiedź jest teraz bardziej przydatna.
Zrobiłem to w ten sposób, aby był kompatybilny ze starymi przeglądarkami.
// For really old browser's 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;
}
Więcej informacji o współrzędnych w JavaScript tutaj: http://javascript.info/tutorial/coordinates
Aby uzyskać całkowite przesunięcie elementu, można rekurencyjnie sumować wszystkie przesunięcia nadrzędne:
function getParentOffsets(el): number {
if (el.offsetParent) {
return el.offsetParent.offsetTop + getParentOffset(el.offsetParent);
} else {
return 0;
}
}
dzięki tej funkcji narzędzia całkowite górne przesunięcie elementu dom wynosi:
el.offsetTop + getParentOffsets(el);
/**
*
* @param {HTMLElement} el
* @return {{top: number, left: number}}
*/
function getDocumentOffsetPosition(el) {
var position = {
top: el.offsetTop,
left: el.offsetLeft
};
if (el.offsetParent) {
var parentPosition = getDocumentOffsetPosition(el.offsetParent);
position.top += parentPosition.top;
position.left += parentPosition.left;
}
return position;
}
Dziękuję ThinkingStiff za odpowiedź , to tylko inna wersja.
Z powodzeniem zastosowałem rozwiązanie Andy E do pozycjonowania modalu bootstrap 2 w zależności od tego, który link w wierszu tabeli kliknie użytkownik. Strona jest stroną Tapestry 5, a javascript poniżej jest importowany do klasy strony 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);
}
Mój kod modalny:
<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>
Link w pętli:
<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>
I kod Java w klasie strony:
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');");
}
});
}
Mam nadzieję, że to komuś pomoże, ten post pomógł.
Po wielu badaniach i testach wydaje się, że to działa
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 };
}
Pomyślałem, że też to wyrzucę.
Nie byłem w stanie przetestować go w starszych przeglądarkach, ale działa on w najnowszej wersji 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()};
};
Ponieważ różne przeglądarki wyświetlają ramki, wypełnienia, marginesy itp. W różny sposób. Napisałem małą funkcję, aby pobrać górne i lewe pozycje określonego elementu w każdym elemencie głównym, który chcesz w dokładnym wymiarze:
function getTop(root, offset) {
var rootRect = root.getBoundingClientRect();
var offsetRect = offset.getBoundingClientRect();
return offsetRect.top - rootRect.top;
}
Aby odzyskać lewą pozycję, musisz wrócić:
return offsetRect.left - rootRect.left;
Uzyskaj pozycję div względem lewej i górnej
var elm = $('#div_id'); //get the div
var posY_top = elm.offset().top; //get the position from top
var posX_left = elm.offset().left; //get the position from left
el.offsetTop
i el.offsetLeft
(OP nie mówi nic o jQuery ... Nie jestem pewien, dlaczego twoja odpowiedź używa jQuery też ...)