Récupérer la position (X, Y) d'un élément HTML par rapport à la fenêtre du navigateur

1570

Je veux savoir comment obtenir la position X et Y des éléments HTML tels que imget diven JavaScript par rapport à la fenêtre du navigateur.

Jack
la source
134
Par rapport à quoi?
AnthonyWJones
4
j'avais utilisé ces 7 lignes de code qui fonctionnent dans tous les navigateurs avec des divergences dans ie5,6,7 (je ne me souviens pas avoir eu de problème ... peut être du type doc) ... quirksmode.org/js/findpos. html et je l'utilisais beaucoup depuis tant d'années. peut être sombody peut pointer les défauts le cas échéant.
Jayapal Chandran
99
@AnthonyWJones, je suppose que vous aviez besoin du plaisir pédant, mais il est évident, comme c'est toujours le cas non spécifié, que l'OP se réfère au cas le plus général, ou se réfère aux coordonnées de la fenêtre du navigateur.
Motes
7
@Mote, non, ce n'est pas si évident. Laisser de côté l'inférence, la subjectivité et les faux axiomes. Il peut être relatif à la fenêtre d'affichage ou au haut de la page (alias document.documentElement).
Andre Figueiredo
16
Par défaut, le plus général n'est pas une inférence, c'est la progression logique, donc ce serait relatif à la fenêtre, ou "haut de page" pourrait être le terme comme vous le dites. Dans la théorie des jeux, il est codifié comme un concept appelé point de Schelling. Assurez-vous de préciser quand vous ne voulez pas parler du cas le plus général.
Motes

Réponses:

1796

La bonne approche consiste à utiliser element.getBoundingClientRect():

var rect = element.getBoundingClientRect();
console.log(rect.top, rect.right, rect.bottom, rect.left);

Internet Explorer prend en charge cela depuis aussi longtemps que vous vous en souciez et il a finalement été normalisé dans les vues CSSOM . Tous les autres navigateurs l'ont adopté il y a longtemps .

Certains navigateurs renvoient également des propriétés de hauteur et de largeur, bien que ce ne soit pas standard. Si vous êtes préoccupé par la compatibilité des anciens navigateurs, consultez les révisions de cette réponse pour une implémentation dégradée optimisée.

Les valeurs renvoyées par element.getBoundingClientRect()sont relatives à la fenêtre. Si vous en avez besoin par rapport à un autre élément, soustrayez simplement un rectangle de l'autre:

var bodyRect = document.body.getBoundingClientRect(),
    elemRect = element.getBoundingClientRect(),
    offset   = elemRect.top - bodyRect.top;

alert('Element is ' + offset + ' vertical pixels from <body>');
Andy E
la source
143
Un article très intéressant sur les coordonnées JS . Cet article n'est mentionné nulle part sur SO, il devrait ..
e2-e4
6
Ne fonctionne pas ... c'est 8 pixels à la verticale et à l'horizontale, en raison de la marge de 8 pixels dans le corps. La solution de Meouw fonctionne parfaitement. Si vous voulez, j'ai un petit test pour démontrer le problème.
CpnCrunch du
3
En fait, je pense que cela dépend de ce que vous voulez réellement obtenir. La question est un peu ambiguë. Votre réponse est correcte si vous voulez juste les coordonnées par rapport à la fenêtre ou par rapport à l'élément body, mais cela ne vous aide pas dans le cas où vous voulez la position absolue de l'élément (ce que j'imagine est ce que la plupart des gens veulent) . La réponse de meouw ne vous donne pas cela non plus, sauf si vous supprimez les bits '-scrollLeft' et '-scrollTop'.
CpnCrunch du
4
@CpnCrunch: Je vois ce que tu veux dire maintenant; Je ne savais pas que vous faisiez référence à la dernière partie de ma réponse. Lorsque le corps a une marge, son cadre de délimitation sera compensé par ce nombre de pixels de chaque côté respectif. Dans ce cas, vous devez également soustraire le style de marge calculé des coordonnées du corps. Il convient de noter que les réinitialisations CSS suppriment la marge <body>, cependant.
Andy E
3
element.getBoundingClientRect().topne renvoie pas le décalage supérieur correct dans le cas d'un position: fixedélément dans iOS (iPhone 8) s'il contient un élément d'entrée focalisé et que le clavier virtuel a glissé vers le haut. Par exemple, si le décalage réel par rapport au haut de la fenêtre est de 200 pixels, il le signalera comme 420 pixels, où 220 pixels est la hauteur du clavier virtuel. Si vous définissez l'élément position: absoluteet le positionnez à la même position que s'il était fixe, vous obtiendrez les 200 pixels corrects. Je ne connais pas de solution à cela.
Jānis Elmeris
327

Les bibliothèques vont jusqu'à certaines longueurs pour obtenir des décalages précis pour un élément.
voici une fonction simple qui fait le travail dans toutes les circonstances que j'ai essayées.

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; 
meouw
la source
14
changez: el = el.parentNode en: el = el.offsetParent; et cela semble fonctionner pour les iframes imbriqués maintenant ... Je pense que c'est ce que vous vouliez?
Adam
4
Cette solution est incomplète. Essayez de mettre une bordure épaisse sur un div et imbriquez-le plusieurs fois. Dans n'importe quelle version de Firefox (2-5), il sera désactivé par la largeur de bordure (même en mode de conformité aux normes).
ck_
3
n'y a-t-il pas une façon plus propre de le faire, comme une fonction el.totalOffsetLeft et totalOffsetTop? jQuery a certainement cette option mais c'est dommage qu'il n'y ait pas de méthode officielle pour cela, devons-nous attendre DOM4? Je suis en train de créer une application HTML5 canvas, donc je pense que la meilleure solution serait d'insérer un élément canvas dans un iframe afin que je puisse obtenir de vraies coordonnées avec clientX et clientY lorsque nous cliquons sur l'élément.
baptx
1
Donc, @Adam et meouw, dites-vous que le premier bloc de code est incorrect? Alors pourquoi ne pas supprimer tout cela? (Cette question a vu pas mal de téléspectateurs au fil des ans; peut-être bien d'enlever des choses s'ils se trompent?)
Arjan
2
@baptx: Je sais que c'est une réponse tardive, mais je n'ai pas vu votre commentaire plus tôt. Voir ma réponse ci-dessous pour une alternative plus conforme aux normes - vous n'avez même pas vraiment besoin du code de secours, car les navigateurs le prennent en charge depuis longtemps.
Andy E
242

Cela a fonctionné pour moi (modifié à partir de la réponse la plus votée):

function getOffset(el) {
  const rect = el.getBoundingClientRect();
  return {
    left: rect.left + window.scrollX,
    top: rect.top + window.scrollY
  };
}

En utilisant cela, nous pouvons appeler

getOffset(element).left

ou

getOffset(element).top
Adam Grant
la source
1
Seule cette solution fonctionne pour moi lorsque l'élément est placé dans divle centre à l'aide de CSS top:50%, left:50%; transform:translate(-50%, -50%);... excellent. D'accord avec la question @ScottBiggs. La logique est de rechercher la simplicité.
nelek
3
peut-être que ce n'est pas un gagnant car c'est la même chose que la solution d'Andy E, mais ne fonctionne pas dans les anciens navigateurs <IE9
niltoid
getBoundingClientRect provoque un pic de repeinture massif dans mes graphiques de performances
frumbert
1
Vous devez définir l' rectaide var, letou const. Sinon, il est défini comme window.rect.
hev1
2
left: rect.left + window.scrollX + (rect.width / 2), top: rect.top + window.scrollY + (rect.height / 2)renverra la position médiane d'un élément
abhishake
97

Si vous voulez que cela se fasse uniquement en javascript , voici quelques doublons utilisantgetBoundingClientRect()

window.scrollY + document.querySelector('#elementId').getBoundingClientRect().top // Y

window.scrollX + document.querySelector('#elementId').getBoundingClientRect().left // X

La première ligne renverra offsetTopdire Y par rapport au document. La deuxième ligne renverra offsetLeftdisons X par rapport au document.

getBoundingClientRect() est une fonction javascript qui renvoie la position de l'élément par rapport à la fenêtre d'affichage de la fenêtre.

Mustkeem K
la source
2
Cela devrait être la solution. Il n'y a pas de code long ici, c'est compréhensible et très précis!
E Alexis MT
2
que faire si l'élément se trouve dans un élément déroulant? La seule façon de le faire est de additionner offsetTop de tous les éléments parents, comme d'autres réponses le suggèrent
Dmitri Pisarev
Comme l'affirme Dmitri, ceci est incorrect et très imparfait. Il ne devrait pas avoir autant de votes positifs que lui. Il sera TOUJOURS également dans un élément défilable car le document lui-même peut être défilé. En d'autres termes, à moins que le document entier ne rentre dans la fenêtre, il sera toujours incorrect si l'utilisateur a fait défiler la fenêtre principale.
Lev
46

Les éléments HTML de la plupart des navigateurs auront: -

offsetLeft
offsetTop

Ceux-ci spécifient la position de l'élément par rapport à son parent le plus proche qui a une disposition. Ce parent est souvent accessible via la propriété offsetParent.

IE et FF3 ont

clientLeft
clientTop

Ces propriétés sont moins courantes, elles spécifient une position des éléments avec sa zone cliente parents (la zone rembourrée fait partie de la zone cliente mais pas la bordure et la marge).

AnthonyWJones
la source
36

Si la page inclut - au moins - tout "DIV", la fonction donnée par meouw jette la valeur "Y" au-delà des limites de page actuelles. Afin de trouver la position exacte, vous devez gérer à la fois offsetParent et parentNode.

Essayez le code donné ci-dessous (il est vérifié pour 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
la source
1
comme avec les autres solutions, même avec FF 24.4, le code ci-dessus ne fonctionne pas lorsque des largeurs de bordure existent dans le cadre de la disposition de positionnement.
voler
Vous êtes un épargnant de vie. Je travaille sur quelques bugs GWT assez profonds en ce moment et le jeter dans une méthode JSNI résout tous mes problèmes !!
Will Byrne
35

Vous pouvez ajouter deux propriétés pour Element.prototypeobtenir le haut / gauche de n'importe quel élément.

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

Cela s'appelle comme ceci:

var x = document.getElementById( 'myDiv' ).documentOffsetLeft;

Voici une démo comparant les résultats à ceux de jQuery offset().topet .left: http://jsfiddle.net/ThinkingStiff/3G7EZ/

ThinkingStiff
la source
14
la modification Element.prototypeest généralement considérée comme une mauvaise idée . Cela rend le code très difficile à maintenir. De plus, ce code ne tient pas compte du défilement.
Jared Forsyth
23

Pour récupérer efficacement la position par rapport à la page, et sans utiliser de fonction récursive: (inclut également 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
la source
L'inclusion du tout important scrollTop / scrollLeft rend cette réponse un peu plus correcte.
scunliffe
19

Que diriez-vous de quelque chose comme ça, en passant l'ID de l'élément et il retournera la gauche ou le haut, nous pouvons également les combiner:

1) trouver à gauche

function findLeft(element) {
  var rec = document.getElementById(element).getBoundingClientRect();
  return rec.left + window.scrollX;
} //call it like findLeft('#header');

2) trouver le top

function findTop(element) {
  var rec = document.getElementById(element).getBoundingClientRect();
  return rec.top + window.scrollY;
} //call it like findTop('#header');

ou 3) trouver gauche et haut ensemble

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
la source
16

Vous pourriez être mieux servi en utilisant un framework JavaScript, qui a des fonctions pour renvoyer ces informations (et bien plus encore!) De manière indépendante du navigateur. Voici quelques-uns:

Avec ces cadres, vous pourriez faire quelque chose comme: $('id-of-img').top pour obtenir la coordonnée y-pixel de l'image.

Shalom Craimer
la source
2
@Costa Ils utilisent tous ce type de fonctions, bien qu'il s'agisse d'un domaine en évolution, car les différents navigateurs se conforment différemment à la spécification. Une grande partie du code traite de la "correction" des problèmes. Vous devriez vraiment regarder le code source.
Shalom Craimer
18
@scraimer: jQuery et YUI ne sont PAS des frameworks !!! Ce sont des bibliothèques ! Ce n'est pas la même chose. Citant jquery.com : "jQuery est une bibliothèque JavaScript rapide, petite et riche en fonctionnalités ." Citant yuilibrary.com (vous pouvez aussi voir le mot "magique" dans l'URL ...): "YUI est une bibliothèque JavaScript et CSS open source gratuite pour créer des applications Web riches en interactivité."
Sk8erPeter
29
Vous devez donc utiliser d'énormes bibliothèques uniquement pour cette tâche simple? Pas nécessaire. Je déteste que chaque fois que je veux faire quelque chose avec js, j'obtiens ces solutions jQuery. jQuery n'est pas JS!
Opptatt Jobber
1
@OpptattJobber Je suis d'accord ... JQuery n'est pas javascript. Il s'appuie sur Javascript mais c'est un langage de programmation complètement différent.
Nick Manning
5
@NickManning Ce n'est pas un nouveau langage, c'est une couche d'abstraction pour le DOM qui élimine d'avoir à écrire des solutions de contournement de compatibilité et de performances directement dans votre code. Si vous n'utilisez pas jQuery, vous devez de toute façon utiliser un type de couche d'abstraction.
Ginman
15

jQuery .offset () obtiendra les coordonnées actuelles du premier élément, ou définira les coordonnées de chaque élément, dans l'ensemble des éléments correspondants, par rapport au document.

akauppi
la source
Pour une raison quelconque, il ne donne pas les mêmes résultats dans IE que les autres navigateurs. Je pense que dans IE, il donne une position par rapport à la fenêtre, donc si vous faites défiler, vous obtiendrez des résultats différents dans IE par rapport aux autres
Abdul Rahim Haddad
1
offset () est confondu par le zoom, au moins dans Android Chrome, il est donc inutile. stackoverflow.com/a/11396681/1691517 montre une manière tolérante au zoom.
Timo Kähkönen
10

J'ai pris la réponse de @ meouw, ajouté dans le clientLeft qui permet la frontière, puis créé trois versions:

getAbsoluteOffsetFromBody - similaire à @ meouw, cela obtient la position absolue par rapport au corps ou à l'élément html du document (selon le mode excentrique)

getAbsoluteOffsetFromGivenElement - retourne la position absolue par rapport à l'élément donné (relativeEl). Notez que l'élément donné doit contenir l'élément el, sinon cela se comportera de la même manière que getAbsoluteOffsetFromBody. Cela est utile si vous avez deux éléments contenus dans un autre élément (connu) (éventuellement plusieurs nœuds dans l'arborescence des nœuds) et que vous souhaitez leur donner la même position.

getAbsoluteOffsetFromRelative - renvoie la position absolue par rapport au premier élément parent avec position: relative. Ceci est similaire à getAbsoluteOffsetFromGivenElement, pour la même raison, mais n'ira que jusqu'au premier élément correspondant.

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

Si vous rencontrez toujours des problèmes, en particulier en ce qui concerne le défilement, vous pouvez essayer de consulter http://www.greywyvern.com/?post=331 - J'ai remarqué au moins un morceau de code discutable dans getStyle qui devrait être correct en supposant que les navigateurs se comportent , mais je n'ai pas testé le reste du tout.

James Carlyle-Clarke
la source
Intéressant ... mais sur Edge getAbsoluteOffsetFromBody (element) .top renvoie une valeur différente lorsque je défile, dans Chrome, il reste cohérent pendant que je défile. Il semble qu'Edge s'ajuste avec la position du défilement. Lorsque l'élément est déroulé hors de la vue, j'obtiens une valeur négative.
Norman
getAbsoluteOffsetFromRelative a fait ma journée, j'ai dû reproduire l'info-bulle bootstrap sans Javascript de bootstrap, cela m'a beaucoup aidé.
Nolyurn
Au cas où Meouw changerait de nom d'utilisateur, liez le lien pour répondre: stackoverflow.com/a/442474/3654837 . (Je ne veux pas cogner ce vieux fil alors oui, je mets en commentaire)
Mukyuu
8

si vous utilisez jQuery, le plugin dimensions est excellent et vous permet de spécifier exactement ce que vous voulez.

par exemple

Position relative, position absolue, position absolue sans rembourrage, avec rembourrage ...

Cela continue, disons simplement qu'il y a beaucoup à faire.

De plus, l'avantage de jQuery est sa taille de fichier légère et son utilisation facile, vous ne reviendrez pas à JavaScript sans cela par la suite.

John_
la source
8

C'est le meilleur code que j'ai réussi à créer (fonctionne également dans les iframes, contrairement à l'offset () de jQuery). Il semble que le webkit ait un comportement légèrement différent.

D'après le commentaire de 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
la source
Notez que vous devrez jQueryutiliser $.browser.webkit; Vous devrez jouer avec navigator.userAgentpour faire de même avec pure JavaScript.
SP
2
$ .browser n'est plus disponible dans la dernière version de jQuery
Gus
malheureusement, même avec FF 24.4, le code ci-dessus ne fonctionne pas lorsque des largeurs de bordure existent dans le cadre de la disposition de positionnement.
voler
8

Si vous utilisez jQuery, cela pourrait être une solution simple:

<script>
  var el = $("#element");
  var position = el.position();
  console.log( "left: " + position.left + ", top: " + position.top );
</script>
Adam Boostani
la source
8

Différence entre petit et petit

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

Regardez un exemple de coordonnées: http://javascript.info/tutorial/coordinates

KingRider
la source
Cela ne fonctionne malheureusement pas pour tous les éléments que l'on peut trouver dans un document HTML moderne. Embarqués <svg>éléments, par exemple, ne sont pas offsetLeft, offsetTopet les offsetParentpropriétés.
Jonathan Eunice
C'est juste DIVou IMGpas SVG. Voir "Regardez un exemple de coordonnées"? vous pouvez trouver l'objet svg est l'appel de position getOffsetRect , essayez de dépendre du travail de l'objet.
KingRider
7

L'approche la plus propre que j'ai trouvée est une version simplifiée de la technique utilisée par jQuery offset. Semblable à certaines des autres réponses, il commence par getBoundingClientRect; il utilise ensuite le windowet le documentElementpour ajuster la position du défilement ainsi que des éléments comme la marge sur le body(souvent la valeur par défaut).

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
la source
6

Bien que cela soit très probablement perdu au bas de tant de réponses, les meilleures solutions ici ne fonctionnaient pas pour moi.
Pour autant que je sache, aucune des autres réponses n'aurait aidé.

Situation :
dans une page HTML5, j'avais un menu qui était un élément nav à l'intérieur d'un en-tête (pas l'en-tête mais un en-tête dans un autre élément).
Je voulais que la navigation reste en haut une fois qu'un utilisateur y avait fait défiler, mais avant cela, l'en-tête était positionné de manière absolue (donc je pouvais le superposer quelque chose d'autre).
Les solutions ci-dessus n'ont jamais déclenché de changement car .offsetTop n'allait pas changer car il s'agissait d'un élément positionné en absolu. De plus, la propriété .scrollTop était simplement le haut de l'élément le plus haut ... c'est-à-dire 0 et serait toujours 0.
Tous les tests que j'ai effectués en utilisant ces deux (et la même chose avec les résultats de getBoundingClientRect) ne me diraient pas si le haut de la barre de navigation a jamais défilé vers le haut de la page visible (encore une fois, comme indiqué dans la console, ils sont simplement restés les mêmes chiffres pendant le défilement eu lieu).

Solution
La solution pour moi était d'utiliser

window.visualViewport.pageTop

La valeur de la propriété pageTop reflète la section visible de l'écran, ce qui me permet donc de suivre où un élément se réfère aux limites de la zone visible.

Il est probablement inutile de le dire, chaque fois que je traite du défilement, je m'attends à utiliser cette solution pour répondre par programmation au mouvement des éléments défilés.
J'espère que cela aide quelqu'un d'autre.
REMARQUE IMPORTANTE: cela semble fonctionner dans Chrome et Opera actuellement et certainement pas dans Firefox (6-2018) ... jusqu'à ce que Firefox prenne en charge visualViewport, je recommande de NE PAS utiliser cette méthode, (et j'espère qu'ils le feront bientôt ... cela fait beaucoup plus de sens que les autres).


MISE À JOUR:
Juste une note concernant cette solution.
Bien que je trouve toujours ce que j'ai découvert très utile pour les situations dans lesquelles "... répondre par programmation au mouvement des éléments en cours de défilement". est applicable. La meilleure solution au problème que j'avais était d'utiliser CSS pour définir position: collantsur l'élément. En utilisant sticky, vous pouvez laisser un élément en haut sans utiliser javascript (REMARQUE: il y a des moments où cela ne fonctionnera pas aussi efficacement que de changer l'élément en fixe, mais pour la plupart des utilisations, l'approche collante sera probablement supérieure)

UPDATE01:
J'ai donc réalisé que pour une page différente, j'avais une exigence où je devais détecter la position d'un élément dans une configuration de défilement légèrement complexe (parallaxe plus éléments qui défilent dans le cadre d'un message). J'ai réalisé dans ce scénario que les éléments suivants fournissaient la valeur que j'utilisais pour déterminer quand faire quelque chose:

  let bodyElement = document.getElementsByTagName('body')[0];
  let elementToTrack = bodyElement.querySelector('.trackme');
  trackedObjPos = elementToTrack.getBoundingClientRect().top;
  if(trackedObjPos > 264)
  {
    bodyElement.style.cssText = '';
  }

J'espère que cette réponse est plus largement utile maintenant.

MER
la source
cela est très susceptible d'être au sommet de tant de réponses lorsque vous cliquez sur actif pour trier les réponses
lucchi
@lucchi: Eh bien, je suppose que je pourrais supprimer le texte @ en haut ... même si je me suis retrouvé sur celui-ci à nouveau et j'ai réalisé que j'avais trouvé une meilleure solution à mon propre problème ... bien que ma réponse soit plus note de bas de page pour les réponses plus complètes. Je soupçonne que mes exigences n'étaient peut-être pas si courantes que les autres réponses ne fonctionnaient pas pour moi?
MER
1
J'apprécie votre réponse de toute façon, c'était juste pour vous dire que, quiconque veut savoir que vous avez écrit quelque chose de nouveau. Vous n'êtes pas seul ....
lucchi
5

Je l'ai fait comme ça, donc c'était compatible avec les anciens navigateurs.

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

Plus d'informations sur les coordonnées en JavaScript ici: http://javascript.info/tutorial/coordinates

Bojan Kseneman
la source
4

Pour obtenir le décalage total d'un élément, vous pouvez résumer récursivement tous les décalages parents:

function getParentOffsets(el): number {
if (el.offsetParent) {
    return el.offsetParent.offsetTop + getParentOffset(el.offsetParent);
} else {
    return 0;
}
}

avec cette fonction d'utilité, le décalage supérieur total d'un élément dom est:

el.offsetTop + getParentOffsets(el);
Kevin K.
la source
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;
}

Merci ThinkingStiff pour la réponse , ce n'est qu'une autre version de celui-ci.

Văn Quyết
la source
2

J'ai utilisé avec succès la solution d'Andy E pour positionner un modal bootstrap 2 en fonction du lien dans une ligne de tableau sur lequel un utilisateur clique. La page est une page Tapestry 5 et le javascript ci-dessous est importé dans la classe de page 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);

}

Mon code modal:

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

Le lien dans la boucle:

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

Et le code java dans la classe de page:

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');");
        }
    });
}

J'espère que cela aide quelqu'un, ce message a aidé.

Mark Espinoza
la source
Ce code js m'a aidé, j'ai une ancienne application webforms utilisant plusieurs espaces réservés injectant des pages .aspx et je n'arrivais pas à comprendre comment ajouterChild () d'une boîte popup que je créais pour la mettre dans la fenêtre car l'arborescence DOM entière est détraquée avec les espaces réservés multiples et le balisage incorrect. Utiliser le body.getBoundingClientRect () puis utiliser son top dans le positionnement était ce dont j'avais besoin. Merci.
Brad Martin
1

Après de nombreuses recherches et tests, cela semble fonctionner

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

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

kernowcode
la source
1

Je pensais juste que je jetterais ça aussi.
Je n'ai pas pu le tester dans les anciens navigateurs, mais cela fonctionne dans le dernier du top 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
la source
0

Étant donné que différents navigateurs affichent la bordure, le remplissage, la marge, etc. de manière différente. J'ai écrit une petite fonction pour récupérer les positions supérieure et gauche de l'élément spécifique dans chaque élément racine que vous souhaitez dans une dimension précise:

function getTop(root, offset) {
    var rootRect = root.getBoundingClientRect();
    var offsetRect = offset.getBoundingClientRect();
    return offsetRect.top - rootRect.top;
}

Pour récupérer la position gauche, vous devez retourner:

    return offsetRect.left - rootRect.left;
samadadi
la source
-1

Obtenez la position de div par rapport à gauche et en haut

  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
la source
J'ai voté contre parce que le bas et la droite ne sont pas des propriétés de décalage ()
MacroMan
@MacroMan, je respecte votre décision mais j'ai déjà expliqué dans le texte que "le code en bas et à droite n'est pas testé". De plus, je mettrai à jour mon code très bientôt.
Vishal
Je pense que c'est el.offsetTopet el.offsetLeft(l'OP ne dit rien sur jQuery ... Je ne sais pas pourquoi votre réponse utilise jQuery non plus ...)
mesqueeb