Czy uzyskać pozycję / przesunięcie elementu względem kontenera nadrzędnego?

125

Jestem przyzwyczajony do pracy z jQuery. Jednak w moim obecnym projekcie używam zepto.js. Zepto nie dostarcza position()metody takiej jak jQuery. Zepto jest dostarczane tylko z offset().

Masz pomysł, jak mogę pobrać przesunięcie kontenera względem rodzica z czystym js lub z Zepto?

matowe
źródło
4
Głosowano w dół, akceptując odpowiedź jQuery (która nie jest nawet oznaczona jako jQuery!) Podczas zadawania pytania JavaScript.
John
@John, czy zauważyłeś tag zepto?
Supersharp
@Supersharp Użyj JavaScripttagu, jeśli używasz czystego JavaScript. Jeśli używasz frameworka lub biblioteki, nie używasz JavaScripttagu. Jeśli masz na imię Jeff, nie nazywałbym cię Sally.
John
@John to opinia, ale tak nie działa SO. Przeczytaj przewodnik po tagach javascript. W takim przypadku należy go używać razem ze znacznikiem biblioteki.
Supersharp
@Supersharp Yeah SO jest w ten sposób niekompetentny; dosłowne napełnianie tagów. 😒︎
Jan

Odpowiedzi:

188

Ostrzeżenie: jQuery, niestandardowy JavaScript

element.offsetLefti element.offsetTopsą czystymi właściwościami javascript do znajdowania pozycji elementu względem jego offsetParent; będący najbliższym elementem nadrzędnym z pozycją relativelubabsolute

Alternatywnie, zawsze możesz użyć Zepto, aby uzyskać pozycję elementu ORAZ jego rodzica i po prostu odjąć te dwa:

var childPos = obj.offset();
var parentPos = obj.parent().offset();
var childOffset = {
    top: childPos.top - parentPos.top,
    left: childPos.left - parentPos.left
}

Ma to tę zaletę, że daje ci odliczenie dziecka względem jego rodzica, nawet jeśli rodzic nie jest w pozycji.

jackwanders
źródło
9
Pozycjonowane elementy staticnie są przesuniętymi rodzicami.
Esailija,
5
nie używaj średników w nawiasach klamrowych, używaj przecinków
Luca Borrione
1
co z ustaloną pozycją? czy są rodzicami offsetowymi?
Ayyash
1
Tak, ustalona pozycja @ Ayya- również tworzy nowy kontekst przesunięcia
mmmeff
9
@vsync jQuery nie jest królem niczego, odkąd ie9 osiągnął EOL w styczniu 2016 roku, mamy ustandaryzowany wybór DOM we wszystkich głównych przeglądarkach, uczymy się czystego js. Również opinie o tym, jakiego frameworka użyć, nie mają miejsca na SO, ponieważ w dużym stopniu zależy to od projektu i platformy docelowej.
Hans Koch
72

w czystym js po prostu użyj offsetLefti offsetTopwłaściwości.
Przykładowe skrzypce: http://jsfiddle.net/WKZ8P/

var elm = document.querySelector('span');
console.log(elm.offsetLeft, elm.offsetTop);
p   { position:relative; left:10px; top:85px; border:1px solid blue; }
span{ position:relative; left:30px; top:35px; border:1px solid red; }
<p>
    <span>paragraph</span>
</p>

Fabrizio Calderan
źródło
Dziękuję Ci. Czy istnieje również sposób, aby uzyskać przesunięcie do elementu parent.parent?
mat.
2
p.offsetTop + p.parentNode.offsetTopMógłbyś zawrzeć to w wywołaniu funkcji rekurencyjnej, podobnie jak PPK zaproponowane kilka lat temu. Możesz zmienić funkcję tak, aby przekroczyła liczbę poziomów, aby przejść w górę, lub użyć selektora do naśladowania $(selector).parents(parentSelector). Wolałbym to rozwiązanie, ponieważ implementacja zepto działa tylko na przeglądarkach obsługujących getBoundingClientsRect- czego nie oferują niektóre telefony komórkowe.
Torsten Walter
Myślałem, że getBoundingClientsRectjest szeroko obsługiwany ... jeśli ktoś wie, które telefony komórkowe go nie obsługują, byłbym zainteresowany (nie mogę znaleźć żadnej tabeli wsparcia dla telefonów komórkowych na ten temat).
Nobita
To prawdopodobnie najbardziej poprawna odpowiedź, mimo że nie jest zaznaczona. Moje osobiste rozwiązanie zmieniło się $(this).offset().leftna this.offsetLeft.
adjwilli
To również naprawia niewłaściwe jQuery.position()zachowanie wewnątrz rodzica przekształconego w 3D, bardzo dziękuję!
rassoh
6

Zrobiłem to w ten sposób w Internet Explorerze.

function getWindowRelativeOffset(parentWindow, elem) {
    var offset = {
        left : 0,
        top : 0
    };
    // relative to the target field's document
    offset.left = elem.getBoundingClientRect().left;
    offset.top = elem.getBoundingClientRect().top;
    // now we will calculate according to the current document, this current
    // document might be same as the document of target field or it may be
    // parent of the document of the target field
    var childWindow = elem.document.frames.window;
    while (childWindow != parentWindow) {
        offset.left = offset.left + childWindow.frameElement.getBoundingClientRect().left;
        offset.top = offset.top + childWindow.frameElement.getBoundingClientRect().top;
        childWindow = childWindow.parent;
    }

    return offset;
};

=================== możesz to tak nazwać

getWindowRelativeOffset(top, inputElement);

Skupiam się na IE tylko w moim celu, ale podobne rzeczy można zrobić dla innych przeglądarek.

Imran Ansari
źródło
1

Dodaj przesunięcie zdarzenia do przesunięcia elementu nadrzędnego, aby uzyskać bezwzględną pozycję przesunięcia zdarzenia.

Przykład :

HTMLElement.addEventListener('mousedown',function(e){

    var offsetX = e.offsetX;
    var offsetY = e.offsetY;

    if( e.target != this ){ // 'this' is our HTMLElement

        offsetX = e.target.offsetLeft + e.offsetX;
        offsetY = e.target.offsetTop + e.offsetY;

    }
}

Jeśli cel zdarzenia nie jest elementem, w którym zdarzenie zostało zarejestrowane, dodaje przesunięcie elementu nadrzędnego do bieżącego przesunięcia zdarzenia w celu obliczenia wartości przesunięcia „Absolutnego”.

Według Mozilla Web API: „Właściwość HTMLElement.offsetLeft tylko do odczytu zwraca liczbę pikseli, o jaką lewy górny róg bieżącego elementu jest przesunięty w lewo w węźle HTMLElement.offsetParent.

Dzieje się tak najczęściej, gdy zarejestrowałeś zdarzenie u rodzica, które zawiera kilka dodatkowych elementów potomnych, na przykład: przycisk z wewnętrzną ikoną lub zakresem tekstu, lielement z wewnętrznymi rozpiętościami. itp...

DeiDei
źródło
A co z rodzicem rodzica? A co z zagnieżdżonymi elementami posiadającymi paski przewijania? A co z poprzednimi elementami z wyświetlaczem: brak? Prawdziwe przesunięcie dokumentu jest prawie niemożliwe do obliczenia, ale silnik renderujący może o tym wiedzieć. Szkoda, że ​​proste właściwości, takie jak pozycja dokumentu, nigdy nie zostały uwzględnione w DOM.
David Spector
@DavidSpector word
1

Przykład

Tak więc, gdybyśmy mieli element podrzędny o identyfikatorze „element-podrzędny” i chcielibyśmy uzyskać jego lewą / górną pozycję względem elementu nadrzędnego, powiedzmy, że element div ma klasę „element-rodzic”, użyj tego kodu.

var position = $("#child-element").offsetRelative("div.item-parent");
alert('left: '+position.left+', top: '+position.top);

Wtyczka Wreszcie, dla właściwej wtyczki (z kilkoma notatkami wyjaśniającymi, co się dzieje):

// offsetRelative (or, if you prefer, positionRelative)
(function($){
    $.fn.offsetRelative = function(top){
        var $this = $(this);
        var $parent = $this.offsetParent();
        var offset = $this.position();
        if(!top) return offset; // Didn't pass a 'top' element 
        else if($parent.get(0).tagName == "BODY") return offset; // Reached top of document
        else if($(top,$parent).length) return offset; // Parent element contains the 'top' element we want the offset to be relative to 
        else if($parent[0] == $(top)[0]) return offset; // Reached the 'top' element we want the offset to be relative to 
        else { // Get parent's relative offset
            var parent_offset = $parent.offsetRelative(top);
            offset.top += parent_offset.top;
            offset.left += parent_offset.left;
            return offset;
        }
    };
    $.fn.positionRelative = function(top){
        return $(this).offsetRelative(top);
    };
}(jQuery));

Uwaga: Możesz tego użyć na zdarzeniu mouseClick lub mouseover

$(this).offsetRelative("div.item-parent");
Irshad Khan
źródło
Nie widzę uwzględnienia przewijania. Może się zdarzyć wiele dziwnych przypadków.
David Spector
1

Mam inne rozwiązanie. Odejmij wartość właściwości nadrzędnej od wartości właściwości podrzędnej

$('child-div').offset().top - $('parent-div').offset().top;
Irshad Khan
źródło
0

Jasne, że jest to łatwe z czystym JS, po prostu zrób to, pracuj również dla stałych i animowanych paneli HTML 5, stworzyłem i wypróbowałem ten kod i działa on dla każdej przeglądarki (w tym IE 8):

<script type="text/javascript">
    function fGetCSSProperty(s, e) {
        try { return s.currentStyle ? s.currentStyle[e] : window.getComputedStyle(s)[e]; }
        catch (x) { return null; } 
    }
    function fGetOffSetParent(s) {
        var a = s.offsetParent || document.body;

        while (a && a.tagName && a != document.body && fGetCSSProperty(a, 'position') == 'static')
            a = a.offsetParent;
        return a;
    }
    function GetPosition(s) {
        var b = fGetOffSetParent(s);

        return { Left: (b.offsetLeft + s.offsetLeft), Top: (b.offsetTop + s.offsetTop) };
    }    
</script>
Moises Conejo
źródło
Czy przetestowałeś wszystkie możliwe przypadki węzłów nadrzędnych i właściwości elementów? Czy to dotyczy przewijania i przewijania zagnieżdżonego?
David Spector