Jsdom: offsetWidth, offsetHeight, offsetTop et offsetLeft

Créé le 1 févr. 2011  ·  32Commentaires  ·  Source: jsdom/jsdom

J'essaie d'implémenter un moteur de rendu côté serveur pour HighCharts. Tout semble fonctionner correctement, à l'exception du balisage SVG résultant où les valeurs x, y, largeur et hauteur sont manquantes partout. Grâce à un certain débogage, je pense avoir identifié le problème dans le fait que offsetWidth, offsetHeight, offsetTop et offsetLeft sont tous indéfinis pour chaque élément.

Ces propriétés devraient-elles être incluses dans une future version ? De plus, existe-t-il une solution de contournement que je peux mettre en œuvre pour le moment ?

css feature layout needs tests

Commentaire le plus utile

J'utilise cet extrait pour le support, ce qui fonctionne pour mon cas. Gardez à l'esprit que c'est loin d'être une mise en œuvre correcte. Notez que window devrait être votre document.parentWindow .

Si vous avez besoin d'une meilleure implémentation, étendez le code à l'aide de la spécification :
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; }
  }
});

Tous les 32 commentaires

Je pense que http://www.w3.org/TR/cssom-view/ , devrait être mis en œuvre.

nous avons commencé à intégrer cssom qui devrait résoudre certains des problèmes SVG.

Ce serait en effet formidable si nous pouvions accéder aux propriétés offset*, nous en aurions besoin pour un projet lié au canevas.

Comment évolue ce dossier ? J'apprécierais également une telle fonctionnalité pour le rendu backend de l'affaissement avec Highcharts.

J'aimerais bien l'avoir aussi :)

Avez-vous des cycles de rechange pour vous aider? Personnellement, je n'ai pas besoin de svg 1.1, mais je serais prêt à aider si vous êtes suffisamment motivés pour y travailler également.

@tmpvar : J'aimerais aider, mais je suis assez nouveau dans ces trucs JS côté serveur.
Peut-être que vous pouvez m'indiquer par où commencer et je pourrais faire de mon mieux pour faire avancer quelque chose :)

Je suis aussi très intéressé par SVG 1.1, pour activer les highcharts sur le serveur. J'aimerais offrir de l'aide, mais je dois d'abord me familiariser avec SVG 1.0 et son implémentation actuelle dans jsdom, ce qui peut prendre un certain temps ;)

Je pense que la première étape ici est de récupérer une copie de la suite de tests (http://www.w3.org/Graphics/SVG/WG/wiki/Test_Suite_Overview) et de la convertir en sans tête (ce qui peut être une entreprise)

Ensuite, nous pouvons ajouter un level2/svg.js et commencer à implémenter.

Je finirai probablement par y arriver, mais ce n'est pas en haut de ma liste de priorités jsdom pour le moment.

@href l'implémentation de svg 1.0 est assez simple. Il passe les tests dans level1/core mais n'implémente rien de spécifique à svg.

Je pense que le getBoundingCLientRect n'est pas implémenté correctement.

J'ai ce code pour calculer un élément svg spécifique :

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

Et le résultat est :

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

Mais, si je teste le même html dans un navigateur Web, le résultat est le suivant :

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

@throrin19 : Lisez le #653.

C'est donc impossible à réaliser ? Jusqu'à présent, j'ai réussi à contourner ce problème via phantomJS mais il est extrêmement lent et si j'ai un gros SVG, il plante.

Je veux dire, ce n'est pas impossible, mais il faudrait écrire tout un moteur de mise en page, et personne n'a eu le temps de le faire.

J'y ai pensé, mais mon cerveau a des spasmes et je dois m'en aller.

Existe-t-il des tests au-delà de l'acide qui pourraient être suivis ?

+1 en trouvant cette fonctionnalité hautement souhaitable

J'utilise cet extrait pour le support, ce qui fonctionne pour mon cas. Gardez à l'esprit que c'est loin d'être une mise en œuvre correcte. Notez que window devrait être votre document.parentWindow .

Si vous avez besoin d'une meilleure implémentation, étendez le code à l'aide de la spécification :
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; }
  }
});

Quelqu'un d'autre a-t-il réussi avec ce que tobyhinloopen a suggéré ?

@tobyhinloopen : L'arrière-plan, l'axe et les étiquettes de mon graphique s'affichent (bien que de manière asymétrique) mais le reste est toujours très confus. Avez-vous pu obtenir un graphique complet ?

J'avais un cas d'utilisation différent où mon implémentation suggérée fonctionnait. Si cela ne fonctionne pas pour vous, prolongez-le. Probablement OffsetHeight/width doit également inclure le rembourrage. Je suggérerais quelques correctifs hacky pour le faire fonctionner, car une implémentation parfaite est difficile à implémenter.

La solution proposée par @tobyhinloopen semble bien mais quand je l'utilise, j'obtiens "ne peut pas affiner la propriété offsetLeft"

@zallek Je pense que vous avez déjà le support offsetLeft alors :) Que renvoie window.HTMLElement.prototype.offsetLeft ? Si c'est tout sauf indéfini, ma solution ne fonctionnera pas

Je voulais juste vous remercier @tobyhinloopen , votre solution m'a été très utile. :)

@tobyhinloopen Merci beaucoup d'avoir publié la solution ! C'est très utile et fonctionne tant que j'utilise une variable pour définir la valeur de décalage et exclure this . this sort indéfini, ce qui est logique étant donné que le contexte n'est pas la classe réelle. C'est ce que vous attendez ?

@jordantomax ceci est lié à l'élément lorsque la fonction est appelée, pas lorsque la fonction est définie. 'this' devrait être l'élément lorsque vous l'appelez à partir de n'importe quel élément html.

Si ce n'est pas le cas, j'aime bien voir un exemple.

@tobyhinloopen Merci pour la réponse rapide. C'est ce à quoi je m'attendrais. Lorsque j'essaie de reproduire mon problème dans codesandbox en utilisant un environnement de navigateur réel, cela fonctionne correctement. Mais lorsque j'utilise jest, je vois this comme indéfini. J'ai créé un petit exemple de dépôt pour vous montrer ce que je vis, peut-être que ma configuration est mauvaise.

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

En relation, j'ai également des problèmes pour remplacer des méthodes comme scrollIntoView utilisant jest et jsdom. Existe-t-il une méthode recommandée pour le faire ?

Merci beaucoup pour votre aide!

Je pense que c'est parce que vous utilisez get: () => {} - La syntaxe de la fonction () => {} gâche votre this . Si vous utilisez plutôt get() {} (recommandé) ou get: function() {} (alternative), cela fonctionnera. @jordantomax

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

Vous utilisez également => dans les it / describe votre suite de tests. Je ne sais pas pour JEST, mais dans MOCHA, ce n'est pas recommandé, car this dans votre it / test / describe a toutes sortes de utilitaires auxquels vous ne pouvez pas accéder si vous utilisez les fonctions fléchées. Par exemple, moka a des choses comme this.slow() , this.timeout(4000) etc.

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

En relation, j'ai également des problèmes pour remplacer des méthodes telles que scrollIntoView en utilisant jest et jsdom. Existe-t-il une méthode recommandée pour le faire ?

Habituellement, il existe une sorte de polyfill disponible sur MDN. Sinon, essayez simplement de l'implémenter en fonction des spécifications d'une manière qui correspond à vos besoins. Si tout échoue, utilisez simplement un environnement de test différent. JSDom a sa place, mais à un moment donné, vous voudrez peut-être envisager un vrai navigateur à la place. De nombreux environnements de test permettent de tester dans de vrais navigateurs (même plusieurs en même temps) tout en rendant compte en temps réel à votre machine de développement. IIRC Karma fait cela -> https://karma-runner.github.io/latest/index.html

J'ai ouvert plusieurs navigateurs (firefox chrome safari internet explorer edge + mobiles dans les simulateurs) en même temps sur ma machine, et ils ont tous rapporté en temps réel à l'aide de Karma + Mocha.

@tobyhinloopen Vous êtes sur tous les comptes ! Merci!! Je ne sais toujours pas pourquoi getComputedStyle est nécessaire, et est-il possible d'écrire un setter avec cette méthode. Pour moi cela semble fonctionner:

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

Merci encore

@jordantomax getComputedStyle récupère le style réel calculé pour toutes les propriétés CSS, peu importe où il a été appliqué (feuille de style, attribut style ou à partir de JS). Je ne sais pas si this.marginTop fait la même chose, mais je suppose que c'est le cas (mais seulement pour la marge évidemment, pas pour tous les accessoires CSS)

@tobyhinloopen Ah, j'ai compris. Merci!

Merci @tobyhinloopen . J'utilise une version encore plus courte. Je pense que getComputedStyle n'est pas vraiment nécessaire car personnellement, je n'ajoute jamais de css à mes tests unitaires à moins qu'il ne soit en ligne, c'est pourquoi .style est assez bon et plus rapide.

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

Cela a-t-il été corrigé, j'obtiens 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

Cela a-t-il été corrigé, j'obtiens offsetWidth = 0

@petran, il n'est pas réaliste de s'attendre à ce que jsdom calcule la largeur de votre SPAN, car cela nécessite de connaître la taille de votre police et toutes les propriétés CSS héritées. Je ne pense pas que ce soit le but de JSDOM. Si vous souhaitez une simulation plus précise d'un navigateur, envisagez d'utiliser un véritable navigateur.

Cette page vous a été utile?
0 / 5 - 0 notes

Questions connexes

domenic picture domenic  ·  3Commentaires

Progyan1997 picture Progyan1997  ·  3Commentaires

khalyomede picture khalyomede  ·  3Commentaires

cg433n picture cg433n  ·  3Commentaires

jacekpl picture jacekpl  ·  4Commentaires