Position / Versatz des Elements relativ zu einem übergeordneten Container abrufen?

125

Ich bin es gewohnt, mit jQuery zu arbeiten. In meinem aktuellen Projekt verwende ich jedoch zepto.js. Zepto bietet keine position()Methode wie jQuery. Zepto kommt nur mit offset().

Haben Sie eine Idee, wie ich den Offset eines Containers relativ zu einem übergeordneten Element mit reinem js oder mit Zepto abrufen kann?

matt
quelle
4
Beim Akzeptieren einer JavaScript-Frage wurde eine jQuery-Antwort (die nicht einmal als jQuery bezeichnet wird!) Abgestimmt.
John
@ John hast du das zepto tag bemerkt?
Supersharp
@Supersharp Verwenden Sie das JavaScriptTag, wenn Sie reines JavaScript verwenden. Wenn Sie einen Rahmen oder Bibliothek verwenden dann tun Sie nicht verwenden , um den JavaScriptTag. Wenn Sie Jeff heißen, würde ich Sie nicht Sally nennen.
John
@ John es ist eine Meinung, aber es ist nicht, wie SO funktioniert. Bitte lesen Sie die Javascript-Tag-Anleitung. In diesem Fall sollte es zusammen mit dem Bibliothekstag verwendet werden.
Supersharp
@Supersharp Yeah SO ist so inkompetent; buchstäbliche Tag Inflation. 😒︎
John

Antworten:

188

Warnung: jQuery, kein Standard-JavaScript

element.offsetLeftund element.offsetTopsind die reinen Javascript-Eigenschaften zum Finden der Position eines Elements in Bezug auf seine offsetParent; das nächste übergeordnete Element mit einer Position von relativeoder seinabsolute

Alternativ können Sie jederzeit Zepto verwenden , um die Position eines Elements UND seines übergeordneten Elements abzurufen und einfach die beiden zu subtrahieren:

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

Dies hat den Vorteil, dass Sie den Versatz eines Kindes relativ zu seinem Elternteil erhalten, auch wenn das Elternteil nicht positioniert ist.

Jackwanders
quelle
9
Die positionierten Elemente staticsind keine versetzten Eltern.
Esailija
5
Verwenden Sie keine Semikolons in geschweiften Klammern, verwenden Sie Kommas
Luca Borrione
1
Was ist mit der festen Position? Sind sie Eltern ausgeglichen?
Ayyash
1
Ja, @ Ayya-Position behoben erstellt auch einen neuen Versatzkontext
mmmeff
9
@vsync jQuery ist nicht der König von irgendetwas, seit ie9 im Januar 2016 EOL erreicht hat. Wir haben eine standardisierte DOM-Auswahl in allen gängigen Browsern. Lernen Sie reine js. Auch Meinungen darüber, welches Framework verwendet werden soll, haben keinen Platz in SO, da dies stark vom Projekt und der Zielplattform abhängt.
Hans Koch
72

in reinen js nur verwenden offsetLeftund eigenschaften offsetTop.
Beispiel Geige: 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
quelle
Danke dir. Gibt es auch eine Möglichkeit, den Offset auf ein parent.parent-Element zu übertragen?
Matt
2
p.offsetTop + p.parentNode.offsetTopSie könnten dies in einen rekursiven Funktionsaufruf einschließen, ähnlich wie PPK vor einigen Jahren vorgeschlagen hat. Sie können die Funktion ändern, um entweder die Anzahl der zu übersteigenden Ebenen zu übergeben oder einen Selektor zum Nachahmen zu verwenden $(selector).parents(parentSelector). Ich würde diese Lösung bevorzugen, da die Zepto-Implementierung nur auf unterstützenden Browsern funktioniert getBoundingClientsRect- was einige Handys nicht tun.
Torsten Walter
Ich dachte, es getBoundingClientsRectwurde weithin unterstützt ... wenn jemand weiß, welche Handys es nicht unterstützen, wäre ich interessiert (ich kann keine Support-Tabelle für Handys darüber finden).
Nobita
Dies ist wahrscheinlich die richtigste Antwort, obwohl sie nicht markiert ist. Meine persönliche Lösung änderte sich $(this).offset().leftzu this.offsetLeft.
Adjwilli
Dies behebt auch falsches jQuery.position()Verhalten innerhalb eines 3D-transformierten Elternteils. Vielen Dank!
Rassoh
6

Ich habe es so im Internet Explorer gemacht.

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

=================== Sie können es so nennen

getWindowRelativeOffset(top, inputElement);

Ich konzentriere mich nur gemäß meinem Fokus auf den IE, aber ähnliche Dinge können für andere Browser getan werden.

Imran Ansari
quelle
1

Fügen Sie den Versatz des Ereignisses zum Versatz des übergeordneten Elements hinzu, um die absolute Versatzposition des Ereignisses zu erhalten.

Ein Beispiel :

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;

    }
}

Wenn das Ereignisziel nicht das Element ist, für das das Ereignis registriert wurde, addiert es den Versatz des übergeordneten Elements zum aktuellen Ereignisversatz, um den Versatzwert "Absolut" zu berechnen.

Laut Mozilla Web API: "Die schreibgeschützte Eigenschaft HTMLElement.offsetLeft gibt die Anzahl der Pixel zurück, um die die obere linke Ecke des aktuellen Elements im Knoten HTMLElement.offsetParent nach links versetzt ist . "

Dies geschieht meistens, wenn Sie ein Ereignis bei einem Elternteil registriert haben, das mehrere untergeordnete liElemente enthält, z. B. eine Schaltfläche mit einem inneren Symbol oder einer Textspanne, ein Element mit inneren Bereichen. etc...

DeiDei
quelle
Was ist mit dem Elternteil des Elternteils? Was ist mit verschachtelten Elementen mit Bildlaufleisten? Was ist mit vorherigen Elementen mit Anzeige: keine? Es ist fast unmöglich, eine echte Dokumentversatzposition zu berechnen, die Rendering-Engine kann sie jedoch erkennen. Schade, dass einfache Eigenschaften wie die Dokumentposition nie in DOM enthalten waren.
David Spector
@DavidSpector Wort
1

Beispiel

Wenn wir also ein untergeordnetes Element mit der ID "untergeordnetes Element" hätten und die Position links / oben relativ zu einem übergeordneten Element ermitteln möchten, sagen wir, ein Div mit der Klasse "Element-übergeordnetes Element" würden wir Verwenden Sie diesen Code.

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

Plugin Zum Schluss für das eigentliche Plugin (mit ein paar Anmerkungen, die erklären, was los ist):

// 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));

Hinweis: Sie können dies für mouseClick- oder mouseover-Ereignisse verwenden

$(this).offsetRelative("div.item-parent");
Irshad Khan
quelle
Ich sehe kein Scrollen. Es gibt viele seltsame Fälle, die passieren können.
David Spector
1

Ich habe eine andere Lösung. Subtrahieren Sie den Wert der übergeordneten Eigenschaft vom Wert der untergeordneten Eigenschaft

$('child-div').offset().top - $('parent-div').offset().top;
Irshad Khan
quelle
0

Sicher ist es einfach mit reinem JS, mach das einfach, arbeite auch für feste und animierte HTML 5-Panels. Ich habe diesen Code erstellt und ausprobiert und er funktioniert für jeden Browser (einschließlich 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
quelle
Haben Sie mit allen möglichen Fällen von übergeordneten Knoten und Elementeigenschaften getestet? Berücksichtigt dies das Scrollen und das verschachtelte Scrollen?
David Spector