Jsdom: offsetWidth, offsetHeight, offsetTop y offsetLeft

Creado en 1 feb. 2011  ·  32Comentarios  ·  Fuente: jsdom/jsdom

Estoy tratando de implementar un renderizador del lado del servidor para HighCharts. Todo parece funcionar bien, excepto que al marcado SVG resultante le faltan los valores x, y, ancho y alto en todo el lugar. A través de algunas depuraciones, creo que he identificado el problema en el hecho de que offsetWidth, offsetHeight, offsetTop y offsetLeft no están definidos para cada elemento.

¿Están estas propiedades programadas para incluirse en una versión futura? Además, ¿hay alguna solución alternativa que pueda implementar en este momento?

css feature layout needs tests

Comentario más útil

Utilizo este fragmento como soporte, que funciona para mi caso. Tenga en cuenta que está lejos de ser una implementación adecuada. Tenga en cuenta que window debe ser su document.parentWindow .

Si necesita una mejor implementación, amplíe el código usando la especificación:
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 comentarios

Creo que sería necesario implementar http://www.w3.org/TR/cssom-view/ .

hemos comenzado a integrar cssom que debería resolver algunos de los problemas de SVG.

De hecho, sería genial si pudiéramos acceder a las propiedades de compensación *, las necesitaríamos para un proyecto relacionado con el lienzo.

¿Cómo está el avance en este tema? También agradecería una característica de este tipo para el renderizado de sag de backend con Highcharts.

Me encantaría tener esto también :)

¿Tienen bicicletas de repuesto para ayudar? Personalmente, no necesito svg 1.1, pero estaría dispuesto a ayudar a conseguirlo si ustedes están lo suficientemente animados para trabajar en él también.

@tmpvar : Me gustaría ayudar, pero soy bastante nuevo en esas cosas JS del lado del servidor.
Tal vez puedas indicarme por dónde empezar y podría hacer todo lo posible para que algo funcione :)

También estoy muy interesado en SVG 1.1, para habilitar highcharts en el servidor. Me gustaría ofrecer ayuda, pero primero necesito familiarizarme con SVG 1.0 y su implementación actual en jsdom, lo que puede llevar un tiempo;)

Creo que el primer paso aquí es tomar una copia del conjunto de pruebas (http://www.w3.org/Graphics/SVG/WG/wiki/Test_Suite_Overview) y convertirlo para que no tenga cabeza (lo que puede ser una gran empresa)

Luego podemos agregar level2/svg.js y comenzar a implementar.

Probablemente llegaré a esto eventualmente, pero no es una prioridad en mi lista de prioridades de jsdom en este momento.

@href, la implementación de svg 1.0 es bastante simple. Pasa las pruebas en level1/core pero en realidad no implementa nada específico de svg.

Creo que getBoundingCLientRect no se implementa correctamente.

Tengo este código para calcular un 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);
            }
        });

Y el resultado es:

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

Pero, si pruebo el mismo html en el navegador web, el resultado es este:

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

@ throrin19 : Lea el n. ° 653.

¿Entonces es imposible de lograr? Hasta ahora logré evitar esta preocupación a través de phantomJS, pero es extremadamente lento y si tengo un SVG grande, se bloquea.

Quiero decir, no es imposible, pero tendríamos que escribir un motor de diseño completo y nadie ha tenido tiempo para hacerlo.

Lo he pensado, pero luego mi cerebro sufre espasmos y tengo que alejarme.

¿Hay pruebas más allá del ácido que se puedan seguir?

+1 al considerar que esta es una característica muy deseable

Utilizo este fragmento como soporte, que funciona para mi caso. Tenga en cuenta que está lejos de ser una implementación adecuada. Tenga en cuenta que window debe ser su document.parentWindow .

Si necesita una mejor implementación, amplíe el código usando la especificación:
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; }
  }
});

¿Alguien más ha tenido éxito con lo que sugirió tobyhinloopen?

@tobyhinloopen : El fondo, el eje y las etiquetas de mi gráfico aparecen (aunque de forma sesgada), pero el resto todavía está muy confuso. ¿Pudiste obtener un gráfico completo?

Tuve un caso de uso diferente en el que funcionó mi implementación sugerida. Si no le funciona, extiéndalo. Probablemente OffsetHeight / width también debe incluir el relleno. Sugeriría algunas correcciones hacky para que funcione, porque una implementación perfecta es difícil de implementar.

La solución propuesta por @tobyhinloopen parece estar bien, pero cuando la uso obtengo "no se puede refinar la propiedad offsetLeft"

@zallek Creo que ya tienes soporte offsetLeft entonces :) ¿Qué devuelve window.HTMLElement.prototype.offsetLeft ? Si es cualquier cosa menos indefinida, mi solución no funcionará

Solo quería darte las gracias @tobyhinloopen , tu solución me fue muy útil. :)

@tobyhinloopen ¡ Muchas gracias por publicar la solución! Es muy útil y funciona siempre que use una variable para establecer el valor de compensación y excluir this . this sale indefinido, lo que tiene sentido dado que el contexto no es la clase real. ¿Es eso lo que esperabas?

@jordantomax esto está vinculado al elemento cuando se llama a la función, no cuando se define la función. 'this' debería ser el elemento cuando lo llame desde cualquier elemento html.

Si no es así, me gustaría ver un ejemplo.

@tobyhinloopen Gracias por la rápida respuesta. Eso es lo que esperaría. Cuando intento reproducir mi problema en codesandbox usando un entorno de navegador real, funciona correctamente. Pero cuando uso broma, veo this como indefinido. Creé un pequeño repositorio de muestra para mostrarte lo que estoy experimentando, tal vez mi configuración sea incorrecta.

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

Relacionado, también tengo problemas para anular métodos como scrollIntoView usando jest y jsdom. ¿Existe alguna forma recomendada de hacerlo?

¡Muchas gracias por su ayuda!

Creo que es porque usas get: () => {} - La sintaxis de la función () => {} confunde con tu this . Si usa get() {} (recomendado) o get: function() {} (alternativa), funcionará. @jordantomax

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

También usa => en los it / describe su suite de prueba. No sé sobre JEST, pero en MOCHA no se recomienda, ya que this en su it / test / describe tiene todo tipo de utilidades a las que no puede acceder si utiliza las funciones de flecha. Por ejemplo, el moca tiene cosas como this.slow() , this.timeout(4000) etc.

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

Relacionado, también tengo problemas para anular métodos como scrollIntoView usando jest y jsdom. ¿Existe alguna forma recomendada de hacerlo?

Por lo general, hay algún tipo de polyfill disponible en MDN. De lo contrario, intente implementarlo según la especificación de una manera que se adapte a sus necesidades. Si todo falla, simplemente use un entorno de prueba diferente. JSDom tiene su lugar, pero en algún momento es posible que desee considerar un navegador real en su lugar. Muchos entornos de prueba permiten realizar pruebas en navegadores reales (incluso múltiples al mismo tiempo) al mismo tiempo que informan en tiempo real a su máquina de desarrollo. IIRC Karma hace esto -> https://karma-runner.github.io/latest/index.html

He tenido varios navegadores abiertos (firefox chrome safari internet explorer edge + móviles en simuladores) todos a la vez en mi máquina, y todos informaron en tiempo real usando Karma + Mocha.

@tobyhinloopen ¡Tienes razón en todas las cuentas! ¡¡Gracias!! Todavía no tengo claro por qué es necesario getComputedStyle , y es posible escribir un setter dado ese método. Para mí, esto parece funcionar:

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

Gracias de nuevo 🙏

@jordantomax getComputedStyle obtiene el estilo real calculado para todas las propiedades CSS, sin importar dónde se haya aplicado (hoja de estilo, atributo style o de JS). No estoy seguro de si this.marginTop hace lo mismo, pero supongo que sí (pero solo para el margen, obviamente, no para todos los accesorios de CSS)

@tobyhinloopen Ah, lo tengo. ¡Gracias!

Gracias @tobyhinloopen . Yo uso una versión aún más corta. Creo que getComputedStyle no es realmente necesario ya que personalmente nunca agrego ningún CSS a mis pruebas unitarias a menos que esté en línea, por lo que .style es lo suficientemente bueno y 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 }
  }
})

¿Se ha solucionado esto, obtengo 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

¿Se ha solucionado esto, obtengo offsetWidth = 0

@petran no es realista esperar que jsdom calcule el ancho de su SPAN, ya que requiere conocer el tamaño de su fuente y todas las propiedades CSS heredadas. No creo que ese sea el objetivo de JSDOM. Si desea una simulación más precisa de un navegador, considere usar un navegador real.

¿Fue útil esta página
0 / 5 - 0 calificaciones