Pobierz pozycję (X, Y) elementu HTML względem okna przeglądarki

1564

Chcę wiedzieć, jak uzyskać pozycję X i Y elementów HTML, takich jak imgi divJavaScript w stosunku do okna przeglądarki.

Jacek
źródło
134
W stosunku do czego?
AnthonyWJones
4
korzystałem z tych 7 linii kodu, który działa we wszystkich przeglądarkach z rozbieżnościami w ie, 6, 6, 7 (nie pamiętałem, że miałem problem ... może to być typ dokumentu) ... quirksmode.org/js/findpos. HTML i używałem go przez wiele lat. może być sombody może wskazywać ewentualne wady.
Jayapal Chandran
98
@AnthonyWJones, przypuszczam, że potrzebowałeś pedantycznej przyjemności, ale jest oczywiste, jak zawsze, gdy nie jest określone, że OP odnosi się do najbardziej ogólnego przypadku lub odnosi się do współrzędnych okna przeglądarki.
Motes
7
@ Uwaga, nie, to nie jest takie oczywiste. Pomiń wnioskowanie, subiektywność i fałszywe aksjomaty. Może być względny w stosunku do rzutni lub góry strony (aka document.documentElement).
Andre Figueiredo
15
Domyślnie najbardziej ogólny nie jest wnioskowanie, jest to logiczny postęp, więc byłby względny w stosunku do okna, lub „góra strony” może być terminem, jak to określasz. W teorii gier jest ona skodyfikowana jako koncepcja zwana punktem Schellinga. Określ, kiedy nie masz na myśli najbardziej ogólnego przypadku.
Motes

Odpowiedzi:

1794

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>');
Andy E.
źródło
143
Bardzo ciekawy artykuł na temat JS współrzędne . Ten artykuł nie jest nigdzie wymieniony na SO, powinien ...
e2-e4
6
Nie działa ... ma 8 pikseli w pionie i poziomie, ze względu na margines 8 pikseli w ciele. Rozwiązanie Meouw działa idealnie. Jeśli chcesz, mam mały test, aby zademonstrować problem.
CpnCrunch
3
Właściwie myślę, że to zależy od tego, co naprawdę chcesz uzyskać. Pytanie jest trochę dwuznaczne. Twoja odpowiedź jest prawidłowa, jeśli chcesz tylko współrzędne względem rzutni lub względem elementu ciała, ale to nie pomaga w przypadku, gdy chcesz bezwzględnej pozycji elementu (co, jak sądzę, jest tym, czego większość ludzi chce). . Odpowiedź meouw też tego nie daje, chyba że usuniesz bity „-scrollLeft” i „-scrollTop”.
CpnCrunch
4
@CpnCrunch: Rozumiem, co masz na myśli; Nie zdawałem sobie sprawy, że odnosisz się do drugiej części mojej odpowiedzi. Gdy ciało ma margines, jego obwiednia zostanie przesunięta o tę liczbę pikseli z każdej odpowiedniej strony. W takim przypadku należy również odjąć obliczony styl marginesu od współrzędnych bryły. Warto jednak zauważyć, że resety CSS usuwają margines <body>.
Andy E
3
element.getBoundingClientRect().topnie zwraca poprawnego górnego przesunięcia w przypadku position: fixedelementu 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: absolutei umieścisz go w tej samej pozycji, jak gdyby był ustalony, otrzymasz poprawną wartość 200 pikseli. Nie znam rozwiązania tego.
Jānis Elmeris
327

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; 
miau
źródło
14
zmień: el = el.parentNode na: el = el.offsetParent; i wydaje się, że teraz działa dla zagnieżdżonych ramek iframe ... Myślę, że to właśnie zamierzałeś?
Adam
4
To rozwiązanie jest niekompletne. Spróbuj nałożyć grubą ramkę na div i zagnieździć ją kilka razy. W każdej wersji Firefoksa (2-5) będzie on wyłączony przez szerokość (-y) ramki (nawet w trybie zgodności ze standardami).
ck_
3
czy nie ma na to lepszego sposobu, takiego jak el.totalOffsetLeft i totalOffsetTop? jQuery z pewnością ma tę opcję, ale szkoda, że ​​nie ma na to oficjalnej metody, czy musimy czekać na DOM4? Tworzę aplikację płótna HTML5, więc myślę, że najlepszym rozwiązaniem byłoby wstawienie elementu canvas w ramce iframe, aby uzyskać rzeczywiste współrzędne z clientX i clientY po kliknięciu elementu.
baptx
1
@Adam i meouw, czy mówisz, że pierwszy blok kodu jest nieprawidłowy? Dlaczego więc nie usunąć tego całkowicie? (To pytanie widziały całkiem sporo widzów na przestrzeni lat; może byłoby miło usunąć rzeczy, jeśli się mylą?)
Arjan
2
@baptx: Wiem, że to późna odpowiedź, ale nie widziałem twojego komentarza wcześniej. Zobacz moją odpowiedź poniżej, aby znaleźć alternatywę zgodną ze standardami - tak naprawdę nie potrzebujesz nawet kodu rezerwowego, ponieważ przeglądarki obsługują go od dłuższego czasu.
Andy E
242

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
Adam Grant
źródło
1
Tylko to rozwiązanie działa dla mnie, gdy element jest umieszczony w 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.
nelek
3
może nie jest zwycięzcą, ponieważ jest taki sam jak rozwiązanie Andy'ego E, ale nie działa w starych przeglądarkach <IE9
niltoid
getBoundingClientRect powoduje ogromny skok odmalowania na moich wykresach wydajności
frumbert
1
Należy zdefiniować rectza pomocą var, letlub const. W przeciwnym razie jest to zdefiniowane jako window.rect.
hev1
2
left: rect.left + window.scrollX + (rect.width / 2), top: rect.top + window.scrollY + (rect.height / 2)zwróci środkową pozycję elementu
abhishake
95

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ść offsetTopY względem dokumentu. Drugi wiersz zwróci „ offsetLeftX” względem dokumentu.

getBoundingClientRect() to funkcja javascript, która zwraca pozycję elementu względem okienka okna.

Mustkeem K
źródło
2
To powinno być rozwiązanie. Nie ma tutaj długiego kodu bs, jest zrozumiały i bardzo dokładny!
E Alexis MT,
2
co jeśli element znajduje się w przewijalnym elemencie? Jedynym sposobem, aby to zrobić, jest zsumowanie offsetTop wszystkich elementów nadrzędnych, jak sugerują inne odpowiedzi
Dmitri Pisarev z
Jak stwierdza Dmitri, jest to niepoprawne i bardzo wadliwe. Nie powinna mieć tyle głosów, ile ma. ZAWSZE znajdzie się w przewijalnym elemencie, ponieważ sam dokument można przewijać. Innymi słowy, chyba że cały dokument mieści się w rzutni, zawsze będzie niepoprawny, jeśli użytkownik w ogóle przewinie okno główne.
Lev
46

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).

AnthonyWJones
źródło
36

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];
};
Refik Ayata
źródło
1
podobnie jak w przypadku innych rozwiązań nawet w FF 24.4, powyższy kod nie działa, gdy szerokości granic istnieją jako część układu pozycjonowania.
okradać
Ratujesz życie. Pracuję teraz nad dość głębokimi błędami GWT i rzucenie tego w metodzie JSNI rozwiązuje wszystkie moje problemy !!
Will Byrne,
35

Możesz dodać dwie właściwości, aby Element.prototypeuzyskać 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().topi .left: http://jsfiddle.net/ThinkingStiff/3G7EZ/

ThinkingStiff
źródło
14
modyfikowanie Element.prototypejest ogólnie uważane za zły pomysł . Prowadzi to do naprawdę trudnego do utrzymania kodu. Ponadto ten kod nie uwzględnia przewijania.
Jared Forsyth
23

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;
Abdul Rahim Haddad
źródło
Dołączenie wszystkich ważnych scrollTop / scrollLeft sprawia, że ​​ta odpowiedź jest nieco bardziej poprawna.
scunliffe,
19

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');
Alireza
źródło
16

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.

Shalom Craimer
źródło
2
@Costa Wszystkie używają tego rodzaju funkcji, chociaż jest to pole ewoluujące, ponieważ różne przeglądarki różnie się dostosowują do specyfikacji. Tyle kodu dotyczy „naprawiania” rzeczy, które się nie udają. Naprawdę powinieneś spojrzeć na kod źródłowy.
Shalom Craimer
18
@scraimer: jQuery i YUI NIEramami !!! To są biblioteki ! To nie to samo. Powołując się na jquery.com : „jQuery to szybka, mała i bogata w funkcje biblioteka JavaScript ”. Powołując się na yuilibrary.com (możesz również zobaczyć słowo „magiczne” w adresie URL…): „YUI to darmowa biblioteka JavaScript i CSS o otwartym kodzie źródłowym do tworzenia bogato interaktywnych aplikacji internetowych”.
Sk8erPeter
29
Więc powinieneś używać ogromnych bibliotek tylko do tego prostego zadania? Niekoniecznie. Nienawidzę tego, że za każdym razem, gdy chcę coś zrobić z js, otrzymuję te rozwiązania jQuery. jQuery to nie JS!
Opptatt Jobber
1
@OpptattJobber Zgadzam się ... JQuery nie jest javascript. Opiera się na JavaScript, ale jest to zupełnie inny język programowania.
Nick Manning
5
@NickManning Nie jest to nowy język, jest to warstwa abstrakcji dla DOM, która eliminuje konieczność zapisywania obejść zgodności i wydajności bezpośrednio w kodzie. Jeśli nie używasz jQuery, i tak powinieneś użyć jakiegoś rodzaju warstwy abstrakcji.
ginman
15

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.

akauppi
źródło
Z jakiegoś powodu nie daje takich samych wyników w IE w porównaniu do innych przeglądarek. Myślę, że w IE podaje pozycję względem okna, więc jeśli przewiniesz, będziesz uzyskiwał inne wyniki w IE w porównaniu do innych
Abdul Rahim Haddad
1
offset () jest mylony przez zoom, przynajmniej w Android Chrome, więc jest bezużyteczny. stackoverflow.com/a/11396681/1691517 pokazuje sposób tolerancji powiększenia.
Timo Kähkönen
10

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.

James Carlyle-Clarke
źródło
Ciekawe ... ale w Edge getAbsoluteOffsetFromBody (element) .top zwraca inną wartość podczas przewijania, w Chrome pozostaje spójny podczas przewijania. Wygląda na to, że Edge dostosowuje się do pozycji przewijania. Kiedy element jest przewijany poza pole widzenia, otrzymuję wartość ujemną.
Norman
getAbsoluteOffsetFromRelative zrobił mój dzień, musiałem odtworzyć podpowiedź bootstrap bez Javascript bootstrap, bardzo mi pomogło.
Nolyurn,
Na wszelki wypadek, jeśli Meouw zmieni nazwę użytkownika, link do odpowiedzi: stackoverflow.com/a/442474/3654837 . (Nie chcę podbijać tego starego wątku, więc tak,
dodam
8

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.

Jan_
źródło
8

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 };
}
Ron Reiter
źródło
Pamiętaj, że musisz jQueryużyć $.browser.webkit; Musisz się pobawić, navigator.userAgentaby zrobić to samo z czystym JavaScript.
SP
2
$ .browser nie jest już dostępny w najnowszej wersji jQuery
Gus
niestety nawet w przypadku FF 24.4 powyższy kod nie działa, gdy istnieją szerokości krawędzi jako część układu pozycjonowania.
okradać
8

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>
Adam Boostani
źródło
8

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

KingRider
źródło
To niestety nie działa dla wszystkich elementów, które można znaleźć w nowoczesnym dokumencie HTML. Wbudowane <svg>elementy, na przykład, nie mam offsetLeft, offsetTopi offsetParentwłaściwości.
Jonathan Eunice,
To jest po prostu DIVlub IMGnie 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.
KingRider,
7

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 windowi documentElementdo 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;

James Montagne
źródło
6

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.

MER
źródło
jest to bardzo prawdopodobne, że znajdzie się na szczycie tak wielu odpowiedzi, gdy klikniesz „ Aktywny”, aby posortować odpowiedzi
lucchi
@lucchi: D cóż, przypuszczam, że mógłbym usunąć tekst @ na górze ... chociaż zdarzyło mi się to powtórzyć i zdałem sobie sprawę, że znalazłem lepsze rozwiązanie dla mojego problemu ... chociaż moja odpowiedź jest bardziej uwaga do pełniejszych odpowiedzi. Podejrzewam, że moje wymagania nie były tak powszechne, że inne odpowiedzi nie działały dla mnie?
MER
1
W każdym razie doceniam twoją odpowiedź, chciałem ci tylko powiedzieć, że każdy chętny będzie wiedział, że napisałeś coś nowego. Nie jesteś sam ....
Lucchi
5

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

Bojan Kseneman
źródło
4

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);
Kevin K.
źródło
3
/**
 *
 * @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.

Văn Quyết
źródło
2

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ł.

Mark Espinoza
źródło
Ten kod js pomógł mi, mam starą aplikację webforms używającą wielu symboli zastępczych do wstrzykiwania stron .aspx i nie mogłem wymyślić, jak appendChild () okna wyskakującego, które tworzyłem, aby dostać się do okienka ekranu, ponieważ całe drzewo DOM jest walone z wieloma symbolami zastępczymi i niepoprawnym znacznikiem. Potrzebne było użycie body.getBoundingClientRect (), a następnie użycie jego górnej pozycji w pozycjonowaniu. Dzięki.
Brad Martin,
1

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 };
}

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

kernowcode
źródło
1

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()};
};
Duncan
źródło
0

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;
samadadi
źródło
-1

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 
Vishal
źródło
Głosowałem w dół, ponieważ dolna i prawa strona nie są właściwościami offset ()
MacroMan
@MacroMan, szanuję twoją decyzję, ale już wyjaśniłem w tekście, że „prawy i dolny kod nie jest testowany”. Również wkrótce zaktualizuję mój kod.
Vishal,
Myślę, że tak el.offsetTopi el.offsetLeft(OP nie mówi nic o jQuery ... Nie jestem pewien, dlaczego twoja odpowiedź używa jQuery też ...)
mesqueeb,