Obter posição / deslocamento do elemento em relação a um contêiner pai?

125

Estou acostumado a trabalhar com jQuery. No meu projeto atual, porém, uso o zepto.js. O Zepto não fornece um position()método como o jQuery. O Zepto vem apenas com offset().

Alguma idéia de como posso recuperar o deslocamento de um contêiner em relação a um pai com js puro ou com o Zepto?

mate
fonte
4
A resposta com jQuery foi recusada com baixa votação (que nem sequer é rotulada como jQuery!) Ao fazer uma pergunta sobre JavaScript.
John
@ John você notou a tag zepto?
Supersharp
@Supersharp Use a JavaScripttag quando estiver usando JavaScript puro. Se você estiver usando uma estrutura ou biblioteca, não usará a JavaScripttag. Se seu nome é Jeff, eu não te chamaria de Sally.
John
@ John é uma opinião, mas não é assim que funciona. Por favor, leia o guia de tags javascript. Nesse caso, ele deve ser usado junto com a tag da biblioteca.
Supersharp
@Supersharp Sim, SO é incompetente assim; inflação literal de tags.
John

Respostas:

188

Aviso: jQuery, não JavaScript padrão

element.offsetLefte element.offsetTopsão as propriedades javascript puras para encontrar a posição de um elemento em relação à sua offsetParent; sendo o elemento pai mais próximo com uma posição relativeouabsolute

Como alternativa, você sempre pode usar o Zepto para obter a posição de um elemento E de seu pai e simplesmente subtrair os dois:

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

Isso tem o benefício de fornecer o deslocamento de um filho em relação ao pai, mesmo que ele não esteja posicionado.

jackwanders
fonte
9
Os elementos posicionados staticnão são pais deslocados.
Esailija
5
não use ponto e vírgula entre chaves, vírgulas uso
Luca Borrione
1
e a posição fixa? eles são pais compensados?
precisa saber é o seguinte
1
Sim, @ Ayya- posição fixa também cria um novo contexto compensar
mmmeff
9
@vsync O jQuery não é o rei de nada desde que a ie9 alcançou a EOL em janeiro de 2016, temos uma seleção DOM padronizada em todos os principais navegadores, aprenda js puro. Também opiniões sobre qual estrutura usar não têm lugar no SO, pois depende muito do projeto e da plataforma de destino.
Hans Koch
72

em pure js basta usar offsetLefte offsetToppropriedades.
Exemplo de violino: 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
fonte
Obrigado. Existe também uma maneira de obter o deslocamento para um elemento parent.parent?
Matt
2
p.offsetTop + p.parentNode.offsetTopVocê pode agrupar isso em uma chamada de função recursiva, como a PPK proposta há alguns anos atrás. Você pode alterar a função para passar o número de níveis a subir ou usar um seletor para imitar $(selector).parents(parentSelector). Eu preferiria esta solução porque a implementação do zepto funciona apenas em navegadores compatíveis getBoundingClientsRect- o que alguns celulares não.
Torsten Walter
Eu pensei que getBoundingClientsRectera amplamente apoiado ... se alguém souber quais celulares não suportam, eu estaria interessado (não consigo encontrar nenhuma tabela de suporte para celulares sobre isso).
Novita
Esta é provavelmente a resposta mais correta, mesmo que não esteja marcada. Minha solução pessoal estava mudando $(this).offset().leftpara this.offsetLeft.
precisa saber é o seguinte
Isso também corrige o jQuery.position()comportamento errado dentro de um pai transformado em 3d, muito obrigado!
rassoh
6

Eu fiz assim no 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;
};

=================== você pode chamá-lo assim

getWindowRelativeOffset(top, inputElement);

Eu me concentro no IE apenas de acordo com meu foco, mas coisas semelhantes podem ser feitas para outros navegadores.

Imran Ansari
fonte
1

Adicione o deslocamento do evento ao deslocamento do elemento pai para obter a posição de deslocamento absoluta do evento.

Um exemplo :

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 o destino do evento não é o elemento no qual o evento foi registrado, ele adiciona o deslocamento do pai ao deslocamento atual do evento para calcular o valor de deslocamento "Absoluto".

De acordo com a Mozilla Web API: "A propriedade somente leitura HTMLElement.offsetLeft retorna o número de pixels que o canto superior esquerdo do elemento atual é deslocado para a esquerda no nó HTMLElement.offsetParent. "

Isso ocorre principalmente quando você registra um evento em um pai que contém vários filhos, por exemplo: um botão com um ícone ou extensão de texto interno, um lielemento com extensões internas. etc ...

DeiDei
fonte
E o pai ou mãe? E quanto aos elementos aninhados com barras de rolagem? E os elementos anteriores com display: none? Uma posição de deslocamento de documento real é quase impossível de calcular, mas o mecanismo de renderização pode conhecê-lo. Propriedades simples ruins como a posição do documento nunca foram incluídas no DOM.
David Spector
@DavidSpector word
1

Exemplo

Portanto, se tivéssemos um elemento filho com um ID de "elemento filho" e desejássemos obter sua posição superior / esquerda em relação a um elemento pai, digamos uma div que tivesse uma classe de "item pai", teríamos use este código.

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

Plugin Finalmente, para o plugin real (com algumas notas expalaining o que está acontecendo):

// 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: Você pode usar isso no evento mouseClick ou mouseover

$(this).offsetRelative("div.item-parent");
Irshad Khan
fonte
Não vejo a rolagem contabilizada. Existem muitos casos estranhos que podem acontecer.
David Spector
1

Eu tenho outra solução. Subtrair o valor da propriedade pai do valor da propriedade filho

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

Claro que é fácil com JS puro, basta fazer isso, trabalhar para painéis HTML 5 fixos e animados também, fiz e experimentei esse código e funciona para qualquer navegador (incluindo o 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
fonte
Você testou com todos os casos possíveis de nós pai e propriedades do elemento? Isso conta para rolagem e rolagem aninhada?
David Spector