Jsdom: offsetWidth, offsetHeight, offsetTop e offsetLeft

Criado em 1 fev. 2011  ·  32Comentários  ·  Fonte: jsdom/jsdom

Estou tentando implementar um renderizador do lado do servidor para HighCharts. Tudo parece funcionar bem, exceto a marcação SVG resultante sem os valores x, y, largura e altura em todo o lugar. Por meio de alguma depuração, acredito que identifiquei o problema no fato de que offsetWidth, offsetHeight, offsetTop e offsetLeft são indefinidos para cada elemento.

Essas propriedades estão programadas para serem incluídas em uma versão futura? Além disso, há uma solução alternativa que eu possa implementar no momento?

css feature layout needs tests

Comentários muito úteis

Eu uso este trecho de suporte, que funciona para o meu caso. Lembre-se de que está longe de ser uma implementação adequada. Observe que window deve ser seu document.parentWindow .

Se você precisar de uma implementação melhor, estenda o código usando a especificação:
http://dev.w3.org/csswg/cssom-view/#dom -htmlelement-offsetleft

Object.defineProperties(window.HTMLElement.prototype, {
  offsetLeft: {
    get: function() { return parseFloat(window.getComputedStyle(this).marginLeft) || 0; }
  },
  offsetTop: {
    get: function() { return parseFloat(window.getComputedStyle(this).marginTop) || 0; }
  },
  offsetHeight: {
    get: function() { return parseFloat(window.getComputedStyle(this).height) || 0; }
  },
  offsetWidth: {
    get: function() { return parseFloat(window.getComputedStyle(this).width) || 0; }
  }
});

Todos 32 comentários

Acho que http://www.w3.org/TR/cssom-view/ precisaria ser implementado.

começamos a integrar o cssom, que deve resolver alguns dos problemas do SVG.

Na verdade, seria ótimo se pudéssemos acessar as propriedades offset *, precisaria delas para um projeto relacionado ao canvas.

Como está o andamento dessa questão? Eu também apreciaria esse recurso para renderização de queda de backend com Highcharts.

Eu adoraria ter isso também :)

Vocês têm bicicletas sobressalentes para ajudar? Pessoalmente, não preciso do svg 1.1, mas estaria disposto a ajudá-lo se vocês estiverem entusiasmados o suficiente para trabalhar nele também.

@tmpvar : Eu gostaria de ajudar, mas sou muito novo nessas coisas de JS do lado do servidor.
Talvez você possa me dar uma dica sobre por onde começar e eu poderia tentar o meu melhor para fazer algo funcionar :)

Também estou muito interessado no SVG 1.1, para habilitar highcharts no servidor. Gostaria de oferecer ajuda, mas primeiro preciso me familiarizar com o SVG 1.0 e sua implementação atual no jsdom, que pode demorar um pouco;)

Acho que o primeiro passo aqui é pegar uma cópia do conjunto de testes (http://www.w3.org/Graphics/SVG/WG/wiki/Test_Suite_Overview) e convertê-lo em headless (o que pode ser uma tarefa difícil)

Então podemos adicionar level2/svg.js e começar a implementação.

Provavelmente irei resolver isso eventualmente, mas não está no topo da minha lista de prioridades do jsdom agora.

@href a implementação do svg 1.0 é bastante simples. Ele passa nos testes em level1/core mas realmente não implementa nada específico de svg.

Eu acho que o getBoundingCLientRect não está implementado corretamente.

Tenho este código para calcular o elemento svg específico:

var html = '<!DOCTYPE html><html><head><title></title></head><body style="margin: 0; padding: 0;"><svg id="svg" xmlns="http://www.w3.org/2000/svg" version="1.1" style="margin: 0; padding: 0;"><rect style="" x="10" y="10" width="100" height="100" rx="0" ry="0"/></svg></body></html>';

        jsdom.env({
            html : html,
            src: [jquery],
            done: function (errors, window) {
                var $ = window.$;
                var clientBox = $("#svg").find(type)[0].getBoundingClientRect();
                console.log(clientBox);
            }
        });

E o resultado é:

{ bottom: 0, height: 0, left: 0, right: 0, top: 0, width: 0 }

Mas, se eu testar o mesmo html no navegador da web, o resultado é este:

{ bottom: 110, height: 100, left: 10, right: 110, top: 10, width: 100 }

@ throrin19 : Leia # 653.

Portanto, é impossível alcançar? Até agora consegui contornar esta preocupação através do phantomJS, mas é extremamente lento e se eu tiver um SVG grande, ele trava.

Quer dizer, não é impossível, mas teríamos que escrever um mecanismo de layout inteiro, e ninguém teve tempo para fazer isso.

Eu pensei sobre isso, mas então meu cérebro teve um espasmo e eu tenho que ir embora.

Existem testes além do ácido que poderiam ser seguidos?

+1 ao descobrir que este é um recurso altamente desejável

Eu uso este trecho de suporte, que funciona para o meu caso. Lembre-se de que está longe de ser uma implementação adequada. Observe que window deve ser seu document.parentWindow .

Se você precisar de uma implementação melhor, estenda o código usando a especificação:
http://dev.w3.org/csswg/cssom-view/#dom -htmlelement-offsetleft

Object.defineProperties(window.HTMLElement.prototype, {
  offsetLeft: {
    get: function() { return parseFloat(window.getComputedStyle(this).marginLeft) || 0; }
  },
  offsetTop: {
    get: function() { return parseFloat(window.getComputedStyle(this).marginTop) || 0; }
  },
  offsetHeight: {
    get: function() { return parseFloat(window.getComputedStyle(this).height) || 0; }
  },
  offsetWidth: {
    get: function() { return parseFloat(window.getComputedStyle(this).width) || 0; }
  }
});

Alguém mais teve sucesso com o que tobyhinloopen sugeriu?

@tobyhinloopen : Meu plano de fundo gráfico, eixo e rótulos aparecem (embora de forma distorcida), mas o resto ainda está muito confuso. Você conseguiu obter um gráfico completo?

Tive um caso de uso diferente em que minha implementação sugerida funcionou. Se não funcionar para você, amplie-o. Provavelmente, OffsetHeight / largura também deve incluir o preenchimento. Eu sugeriria alguns hotfix do hacky para fazer funcionar, porque uma implementação perfeita é difícil de implementar.

A solução proposta por @tobyhinloopen parece boa, mas quando a uso recebo "não é possível refinar a propriedade offsetLeft"

@zallek Acho que você já tem suporte a offsetLeft :) O que window.HTMLElement.prototype.offsetLeft retorna? Se for tudo menos indefinido, minha solução não funcionará

Só queria agradecer @tobyhinloopen , sua solução foi muito útil para mim. :)

@tobyhinloopen Muito obrigado por postar a solução! É muito útil e funciona, desde que eu use uma variável para definir o valor do deslocamento e excluir this . this está saindo indefinido, o que faz sentido, visto que o contexto não é a classe real. É isso que você esperava?

@jordantomax é vinculado ao elemento quando a função é chamada, não quando a função é definida. 'this' deve ser o elemento ao chamá-lo de qualquer elemento html.

Se não, gosto de ver um exemplo.

@tobyhinloopen Obrigado pela resposta rápida. Isso é o que eu esperava. Quando tento reproduzir meu problema no codesandbox usando um ambiente de navegador real, ele funciona corretamente. Mas ao usar jest, vejo this como indefinido. Criei um pequeno repositório de amostra para mostrar o que estou experimentando, talvez minha configuração esteja errada.

https://github.com/jordantomax/jest-htmlelement-prototype-sandbox

Relacionado, também estou tendo problemas para substituir métodos como scrollIntoView usando jest e jsdom. Existe uma maneira recomendada de fazer isso?

Muito obrigado pela sua ajuda!

Eu acho que é porque você usa get: () => {} - A sintaxe da função () => {} mexe com sua this . Se você usar get() {} (recomendado) ou get: function() {} (alternativa), funcionará. @jordantomax

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions#No_separate_this

Você também pode usar => nos it / describe / this em seu it / test / describe tem todos os tipos de utilitários que você não pode acessar se usar as funções de seta. Por exemplo, mocha tem coisas como this.slow() , this.timeout(4000) etc.

https://mochajs.org/#arrow -functions

Relacionado, também estou tendo problemas para substituir métodos como scrollIntoView usando jest e jsdom. Existe uma maneira recomendada de fazer isso?

Normalmente, há algum tipo de polyfill disponível no MDN. Caso contrário, tente implementá-lo com base nas especificações de uma forma que atenda às suas necessidades. Se tudo falhar, basta usar um ambiente de teste diferente. JSDom tem o seu lugar, mas em algum momento você pode querer considerar um navegador real. Muitos ambientes de teste permitem o teste em navegadores reais (até mesmo múltiplos ao mesmo tempo) ao mesmo tempo em que informam em tempo real a sua máquina de desenvolvimento. IIRC Karma faz isso -> https://karma-runner.github.io/latest/index.html

Tive vários navegadores abertos (firefox chrome safari internet explorer edge + mobile em simuladores) ao mesmo tempo na minha máquina, e todos eles relataram em tempo real usando Karma + Mocha.

@tobyhinloopen Certo, você está em todas as contas! Obrigado!! Ainda estou um pouco incerto sobre por que getComputedStyle é necessário, e é possível escrever um setter dado esse método. Para mim, isso parece funcionar:

Object.defineProperties(window.HTMLElement.prototype, {
  offsetTop: {
    get () {
      return this.marginTop
    },
    set (offset) {
      this.marginTop = offset
    }
  }
})

Obrigado de novo 🙏

@jordantomax getComputedStyle busca o estilo real computado para todas as propriedades CSS, não importa onde foi aplicado (folha de estilo, atributo style ou de JS). Não tenho certeza se this.marginTop faz o mesmo, mas acho que sim (mas apenas para a margem, obviamente, não para todos os adereços CSS)

@tobyhinloopen Ah, entendi. Obrigado!

Obrigado @tobyhinloopen . Eu uso uma versão ainda mais curta. Acho que getComputedStyle não é realmente necessário, já que eu pessoalmente nunca adiciono nenhum css aos meus testes de unidade, a menos que seja embutido, e é por isso que .style é bom o suficiente e mais rápido.

Object.defineProperties(window.HTMLElement.prototype, {
  offsetLeft: {
    get () { return parseFloat(this.style.marginLeft) || 0 }
  },
  offsetTop: {
    get () { return parseFloat(this.style.marginTop) || 0 }
  },
  offsetHeight: {
    get () { return parseFloat(this.style.height) || 0 }
  },
  offsetWidth: {
    get () { return parseFloat(this.style.width) || 0 }
  }
})

Isso foi corrigido, estou recebendo offsetWidth = 0

   const dom = new JSDOM(`<!DOCTYPE html><body></body>`);
    let document = dom.window.document
    let span = document.createElement("span");
    span.innerHtml = 'test
    span.style.fontSize = 150 + 'px';
    span.style.fontFamily = 'Arial'
    document.body.appendChild(span)
    console.log(span.offsetWidth) // 0

Isso foi corrigido, estou recebendo offsetWidth = 0

@petran não é realista esperar que jsdom calcule a largura de seu SPAN, uma vez que requer conhecimento do tamanho de sua fonte e de todas as propriedades CSS herdadas. Não acho que seja esse o objetivo do JSDOM. Se você deseja uma simulação mais precisa de um navegador, considere usar um navegador real.

Esta página foi útil?
0 / 5 - 0 avaliações