Ottieni posizione / offset dell'elemento rispetto a un contenitore padre?


125

Sono abituato a lavorare con jQuery. Nel mio progetto attuale, tuttavia, uso zepto.js. Zepto non fornisce un position()metodo come fa jQuery. Zepto viene fornito solo con offset().

Qualche idea su come posso recuperare l'offset di un contenitore rispetto a un genitore con js puro o con Zepto?


4
Sconsigliato l'accettazione di una risposta jQuery (che non è nemmeno etichettata come jQuery!) Quando si pone una domanda JavaScript.
John

@ John hai notato il tag zepto?
Supersharp

@Supersharp Utilizza il JavaScripttag quando utilizzi JavaScript puro. Se stai usando un framework o una libreria, non usi il JavaScripttag. Se ti chiami Jeff non ti chiamerei Sally.
John

@ John è un'opinione ma non è così che funziona. Si prega di leggere la guida ai tag javascript. In questo caso dovrebbe essere usato insieme al tag della libreria.
Supersharp

@Supersharp Yeah COSÌ è incompetente così; inflazione letterale tag. 😒︎
John

Risposte:


188

Avvertenza: jQuery, non JavaScript standard

element.offsetLefte element.offsetTopsono le proprietà javascript pure per trovare la posizione di un elemento rispetto al suo offsetParent; essendo l'elemento genitore più vicino con una posizione di relativeoabsolute

In alternativa, puoi sempre utilizzare Zepto per ottenere la posizione di un elemento E il suo genitore e semplicemente sottrarre i due:

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

Questo ha il vantaggio di darti l'offset di un figlio rispetto al suo genitore anche se il genitore non è posizionato.


9
Gli elementi posizionati staticnon sono genitori offset.
Esailija

5
non usare punto e virgola dentro parentesi graffe, usa virgole
Luca Borrione

1
che dire della posizione fissa? sono genitori compensati?
Ayyash

1
Sì, @ Ayya- la posizione fissa crea anche un nuovo contesto di offset
mmmeff

9
@vsync jQuery non è il re di nulla da quando ie9 ha raggiunto l'EOL nel gennaio 2016, abbiamo una selezione DOM standardizzata in tutti i principali browser, impara js puro. Anche le opinioni su quale framework utilizzare non hanno posto su SO, poiché dipende fortemente dal progetto e dalla piattaforma di destinazione.
Hans Koch

72

in js puro basta usare offsetLefte offsetTopproprietà.
Violino di esempio: 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>


Grazie. C'è anche un modo per ottenere l'offset di un elemento parent.parent?
matt

2
p.offsetTop + p.parentNode.offsetTopPotresti racchiudere questo in una chiamata di funzione ricorsiva molto simile a PPK proposto alcuni anni fa. È possibile modificare la funzione per superare il numero di livelli da aumentare o utilizzare un selettore per imitare $(selector).parents(parentSelector). Preferirei questa soluzione perché l'implementazione di zepto funziona solo su browser che supportano getBoundingClientsRect, cosa che alcuni cellulari non supportano .
Torsten Walter

Pensavo getBoundingClientsRectfosse ampiamente supportato ... se qualcuno sapesse quali cellulari non lo supportano sarei interessato (non riesco a trovare nessuna tabella di supporto per cellulari a riguardo).
Nobita

Questa è probabilmente la risposta più corretta anche se non è contrassegnata. La mia soluzione personale stava cambiando $(this).offset().leftin this.offsetLeft.
adjwilli

Questo risolve anche il jQuery.position()comportamento sbagliato all'interno di un genitore trasformato in 3D, grazie mille!
rassoh

6

L'ho fatto in Internet Explorer.

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

=================== Puoi chiamarlo così

getWindowRelativeOffset(top, inputElement);

Mi concentro su IE solo secondo il mio obiettivo, ma cose simili possono essere fatte per altri browser.


1

Aggiungi l'offset dell'evento all'offset dell'elemento genitore per ottenere la posizione di offset assoluta dell'evento.

Un esempio :

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;

    }
}

Quando il target dell'evento non è l'elemento su cui è stato registrato l'evento, aggiunge l'offset del genitore all'offset dell'evento corrente per calcolare il valore di offset "Assoluto".

Secondo l'API Web di Mozilla: "La proprietà di sola lettura HTMLElement.offsetLeft restituisce il numero di pixel per cui l'angolo superiore sinistro dell'elemento corrente è spostato a sinistra all'interno del nodo HTMLElement.offsetParent. "

Ciò accade principalmente quando hai registrato un evento su un genitore che contiene molti più figli, ad esempio: un pulsante con un'icona interna o un intervallo di testo, un lielemento con intervalli interni. eccetera...


E il genitore del genitore? Che dire degli elementi nidificati con barre di scorrimento? E gli elementi precedenti con display: nessuno? Una vera posizione di offset del documento è quasi impossibile da calcolare, ma il motore di rendering può conoscerla. Peccato che proprietà semplici come la posizione del documento non siano mai state incluse nel DOM.
David Spector

@DavidSpector word

1

Esempio

Quindi, se avessimo un elemento figlio con un id di "elemento-figlio" e volessimo ottenere la sua posizione sinistra / in alto rispetto a un elemento genitore, diciamo un div che aveva una classe di "elemento-genitore", avremmo usa questo codice.

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

Plugin Infine, per il plugin vero e proprio (con alcune note che spiegano cosa sta succedendo):

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

Nota: È possibile utilizzare questo su mouseClick o passaggio del mouse Event

$(this).offsetRelative("div.item-parent");

Non vedo lo scorrimento rappresentato. Ci sono molti casi strani che possono accadere.
David Spector

1

Ho un'altra soluzione. Sottrai il valore della proprietà genitore dal valore della proprietà figlio

$('child-div').offset().top - $('parent-div').offset().top;

0

Certo è facile con JS puro, basta farlo, funziona anche per pannelli HTML 5 fissi e animati, ho creato e provato questo codice e funziona per qualsiasi browser (incluso 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>

Hai testato con tutti i possibili casi di nodi padre e proprietà degli elementi? Questo tiene conto dello scorrimento e dello scorrimento annidato?
David Spector
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.