Obtenir la position / décalage de l'élément par rapport à un conteneur parent?

125

J'ai l'habitude de travailler avec jQuery. Dans mon projet actuel cependant, j'utilise zepto.js. Zepto ne fournit pas de position()méthode comme le fait jQuery. Zepto n'est livré qu'avec offset().

Une idée comment je peux récupérer le décalage d'un conteneur par rapport à un parent avec js pur ou avec Zepto?

mat
la source
4
Accepter une réponse jQuery (qui n'est même pas étiquetée jQuery!) Lors de la pose d'une question JavaScript
John
@John avez-vous remarqué la balise zepto?
Supersharp
@Supersharp Utilisez la JavaScriptbalise lorsque vous utilisez du JavaScript pur. Si vous utilisez un framework ou une bibliothèque, vous n'utilisez pas la JavaScriptbalise. Si vous vous appelez Jeff, je ne vous appellerais pas Sally.
John
@John c'est une opinion mais ce n'est pas ainsi que fonctionne SO. Veuillez lire le guide des balises javascript. Dans ce cas, il doit être utilisé avec l'étiquette de bibliothèque.
Supersharp
@Supersharp Ouais SO est incompétent comme ça; inflation de balise littérale. 😒︎
John

Réponses:

188

Avertissement: jQuery, pas de JavaScript standard

element.offsetLeftet element.offsetTopsont les propriétés javascript pures pour trouver la position d'un élément par rapport à son offsetParent; étant l'élément parent le plus proche avec une position de relativeouabsolute

Alternativement, vous pouvez toujours utiliser Zepto pour obtenir la position d'un élément ET son parent, et simplement soustraire les deux:

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

Cela a l'avantage de vous donner le décalage d'un enfant par rapport à son parent même si le parent n'est pas positionné.

jackwanders
la source
9
Les éléments positionnés staticne sont pas des parents décalés.
Esailija
5
n'utilisez pas de points-virgules entre accolades, utilisez des virgules
Luca Borrione
1
qu'en est-il de la position fixe? sont-ils des parents compensés?
Ayyash
1
Oui, @ Ayya- position fixed crée également un nouveau contexte de décalage
mmmeff
9
@vsync jQuery n'est le roi de rien depuis que ie9 a atteint EOL en janvier 2016, nous avons une sélection DOM standardisée dans tous les principaux navigateurs, apprenez js purs. Aussi les opinions sur le framework à utiliser n'ont pas leur place sur SO, car cela dépend fortement du projet et de la plateforme cible.
Hans Koch
72

en pur js, utilisez simplement offsetLeftet offsetToppropriétés.
Exemple de violon: 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
la source
Je vous remercie. Existe-t-il également un moyen d'obtenir le décalage vers un élément parent.parent?
matt
2
p.offsetTop + p.parentNode.offsetTopVous pouvez envelopper cela dans un appel de fonction récursif un peu comme PPK proposé il y a quelques années. Vous pouvez modifier la fonction pour passer le nombre de niveaux à monter ou utiliser un sélecteur pour imiter $(selector).parents(parentSelector). Je préférerais cette solution car l'implémentation de zepto ne fonctionne que sur les navigateurs prenant en charge getBoundingClientsRect- ce que certains mobiles ne font pas.
Torsten Walter
Je pensais que getBoundingClientsRectc'était largement supporté ... si quelqu'un sait quels mobiles ne le supportent pas, je serais intéressé (je ne trouve aucune table de support pour les mobiles à ce sujet).
Nobita
C'est probablement la réponse la plus correcte même si elle n'est pas marquée. Ma solution personnelle évoluait $(this).offset().leftvers this.offsetLeft.
adjwilli
Cela corrige également les mauvais jQuery.position()comportements à l'intérieur d'un parent transformé en 3D, merci beaucoup!
rassoh
6

Je l'ai fait comme ça dans 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;
};

=================== vous pouvez l'appeler comme ceci

getWindowRelativeOffset(top, inputElement);

Je me concentre sur IE uniquement selon mon objectif, mais des choses similaires peuvent être faites pour d'autres navigateurs.

Imran Ansari
la source
1

Ajoutez le décalage de l'événement au décalage de l'élément parent pour obtenir la position de décalage absolue de l'événement.

Un exemple :

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;

    }
}

Lorsque la cible de l'événement n'est pas l'élément dans lequel l'événement a été enregistré, elle ajoute le décalage du parent au décalage de l'événement en cours afin de calculer la valeur de décalage "Absolu".

Selon Mozilla Web API: "La propriété en lecture seule HTMLElement.offsetLeft renvoie le nombre de pixels dont le coin supérieur gauche de l'élément actuel est décalé vers la gauche dans le nœud HTMLElement.offsetParent. "

Cela se produit principalement lorsque vous avez enregistré un événement sur un parent qui contient plusieurs autres enfants, par exemple: un bouton avec une icône intérieure ou une étendue de texte, un liélément avec des étendues intérieures. etc...

DeiDei
la source
Et le parent du parent? Qu'en est-il des éléments imbriqués ayant des barres de défilement? Qu'en est-il des éléments précédents avec affichage: aucun? Une véritable position de décalage du document est presque impossible à calculer, mais le moteur de rendu peut la connaître. Dommage que des propriétés simples comme la position du document n'aient jamais été incluses dans le DOM.
David Spector
@DavidSpector word
1

Exemple

Donc, si nous avions un élément enfant avec un identifiant de "child-element" et que nous voulions obtenir sa position gauche / supérieure par rapport à un élément parent, disons un div qui avait une classe de "item-parent", nous aurions utilisez ce code.

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

Plugin Enfin, pour le plugin actuel (avec quelques notes expliquant ce qui se passe):

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

Remarque: Vous pouvez utiliser ce sur mouseClick ou mouseover événement

$(this).offsetRelative("div.item-parent");
Irshad Khan
la source
Je ne vois pas le défilement pris en compte. Il y a beaucoup de cas étranges qui peuvent arriver.
David Spector
1

J'ai une autre solution. Soustraire la valeur de la propriété parent de la valeur de la propriété enfant

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

Bien sûr, c'est facile avec le JS pur, il suffit de faire ceci, de travailler aussi pour les panneaux HTML 5 fixes et animés, j'ai créé et essayé ce code et cela fonctionne pour n'importe quel navigateur (y compris 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
la source
Avez-vous testé avec tous les cas possibles de nœuds parents et de propriétés d'élément? Cela tient-il compte du défilement et du défilement imbriqué?
David Spector