Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

offsetWidth, offsetHeight, offsetTop, and offsetLeft #135

Open
compmodder26 opened this issue Feb 1, 2011 · 32 comments
Open

offsetWidth, offsetHeight, offsetTop, and offsetLeft #135

compmodder26 opened this issue Feb 1, 2011 · 32 comments
Labels
css feature layout Blocked on implementing a layout engine needs tests

Comments

@compmodder26
Copy link

I'm trying to implement a server-side renderer for HighCharts. Everything appears to work fine except the resulting SVG markup is missing x, y, width, and height values all over the place. Through some debugging I believe I've pinpointed the problem to the fact that offsetWidth, offsetHeight, offsetTop and offsetLeft are all undefined for each element.

Are these properties slated to be included in a future release? Also, is there a workaround I can implement in the time being?

@tmpvar
Copy link
Member

tmpvar commented Feb 1, 2011

I think http://www.w3.org/TR/cssom-view/, would need to be implemented.

@tmpvar
Copy link
Member

tmpvar commented Jun 19, 2011

we have started integrating cssom which should solve some of the SVG problems.

@jgautheron
Copy link

It would indeed be great if we could access the offset* properties, would need those for a canvas-related project.

@telesma
Copy link

telesma commented Oct 27, 2011

How is the progress in this issue? i would also appreciate such a feature for backend sag rendering with Highcharts.

@damac23
Copy link

damac23 commented Nov 15, 2011

I would love to have this too :)

@tmpvar
Copy link
Member

tmpvar commented Nov 16, 2011

Do you guys have any spare cycles to help? Personally I don't need svg 1.1 but I'd be willing to help land it if you guys are fired up enough to work on it as well.

@damac23
Copy link

damac23 commented Nov 24, 2011

@tmpvar: I'd like to help, but I'm pretty new to those server-side JS-stuff.
Maybe you can hint me on where to start and I could try my best to get something going :)

@href
Copy link

href commented Nov 28, 2011

I'm very interested in SVG 1.1 too, to enable highcharts on the server. I'd like to offer help, but first I need to get acquainted with SVG 1.0 and its current implementation in jsdom, which might take a while ;)

@tmpvar
Copy link
Member

tmpvar commented Nov 29, 2011

I think the first step here is to grab a copy of the test suite (http://www.w3.org/Graphics/SVG/WG/wiki/Test_Suite_Overview) and convert it to be headless (which may be quite an undertaking)

Then we can add a level2/svg.js and start implementing.

I'll probably get around to this eventually, but it is not high on my list of jsdom priorities right now.

@tmpvar
Copy link
Member

tmpvar commented Nov 29, 2011

@href the svg 1.0 implementation is pretty bare. It passes the tests in level1/core but doesn't really implement anything svg specific.

@throrin19
Copy link

I think, the getBoundingCLientRect is not implement correctly.

I've this code to calculate specific svg element :

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

And the result is :

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

But, if i test the same html in web browser, the result is this :

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

@JamesMGreene
Copy link

@throrin19: Read through #653.

@throrin19
Copy link

So it is impossible to achieve? So far I managed to bypass this concern through phantomJS but it is extremely slow and if I have a big SVG, it crashes.

@domenic
Copy link
Member

domenic commented Dec 4, 2013

I mean, it's not impossible, but we'd have to write a whole layout engine, and nobody's had the time to do that.

@tmpvar
Copy link
Member

tmpvar commented Dec 5, 2013

I've thought about it, but then my brain spasms and I have to walk away.

Are there tests beyond acid that could be followed?

@ghost
Copy link

ghost commented Nov 18, 2014

+1 on finding this a highly desirable feature

@tobyhinloopen
Copy link

I use this snippet for support, which works for my case. Keep in mind it is far from a proper implementation. Note that window should be your document.parentWindow.

If you need a better implementation, extend the code using the spec:
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; }
  }
});

@moni-ellevest
Copy link

Has anyone else found success with what tobyhinloopen suggested?

@tobyhinloopen: My graph background, axis, and labels show up (albeit in a skewed way) but the rest is still vey muddled. Were you able to get a complete graph?

@tobyhinloopen
Copy link

I had a different use case where my suggested implementation worked. If it doesn't work for you, extend it. Likely OffsetHeight/width must also include the padding. I would suggest some hacky hotfixing to make it work, because a perfect implementation is difficult to implement.

zpratt pushed a commit to zpratt/async-google-maps that referenced this issue Mar 18, 2015
… correct behavior of `offsetHeight` and `offsetWidth` in jsdom.
mpetrovich added a commit to nextbigsoundinc/imagely that referenced this issue Oct 9, 2015
jsdom does not implement layout-specific functionality like offsetWidth() or getBoundingClientRect() (see jsdom/jsdom#653, jsdom/jsdom#135).

The good news is that jsdom doesn't need to be used at all. Instead, the HTML file can be imported directly, and any external scripts or stylesheets can be inlined as well. PhantomJS will render the resulting HTML without issue, since it's using WebKit behind the scenes.
@zallek
Copy link

zallek commented Jun 19, 2016

The solution proposed by @tobyhinloopen seems fine but when I use it I get "cannot refined property offsetLeft"

@tobyhinloopen
Copy link

@zallek I think you already have offsetLeft support then :) What does window.HTMLElement.prototype.offsetLeft return? If it is anything but undefined, my solution won't work

@tconroy
Copy link

tconroy commented Nov 30, 2016

Just wanted to say thank you @tobyhinloopen, your solution was very useful to me. :)

@Zirro Zirro added the layout Blocked on implementing a layout engine label May 27, 2018
@jordantomax
Copy link

@tobyhinloopen Thanks very much for posting the solution! It's very helpful and does work so long as I use a variable to set the value of offset and exclude this. this is coming out undefined, which kind of makes sense given that the context is not the actual class. Is that what you'd expect?

@tobyhinloopen
Copy link

@jordantomax this is bound to the element when the function is called, not when the function is defined. ‘this’ should be the element when you’re calling it from any html element.

If it doesn’t, I like to see an example.

@jordantomax
Copy link

@tobyhinloopen Thanks for the quick response. That's what I would expect. When I try to reproduce my issue in codesandbox using real browser environment, it works correctly. But when using jest, I'm seeing this as undefined. I created a small sample repo to show you what I'm experiencing, maybe my setup is wrong.

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

Related, I'm also having problems overriding methods like scrollIntoView using jest and jsdom. Is there a recommended way to do it?

Thanks so much for your help!

@tobyhinloopen
Copy link

tobyhinloopen commented May 23, 2019

I think it is because you use get: () => {} - The () => {} function syntax messes with your this. If you use get() {} (recommended) or get: function() {} (alternative) instead, it will work. @jordantomax

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

You also use => in your test suite's it/describe's. I don't know about JEST, but in MOCHA it isn't recommended, since this in your it/test/describe has all kinds of utilities you cannot access if you use the arrow functions. For example, mocha has things like this.slow(), this.timeout(4000) etc.

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

Related, I'm also having problems overriding methods like scrollIntoView using jest and jsdom. Is there a recommended way to do it?

Usually there is some kind of polyfill available on MDN. Otherwise, just attempt to implement it based on the spec in a way that suits your needs. If all fails, just use a different testing environment. JSDom has its place, but at some point you might want to consider a real browser instead. Many testing environments allow testing in real browsers (even multiples at the same time) while also reporting back realtime to your development machine. IIRC Karma does this -> https://karma-runner.github.io/latest/index.html

I have had multiple browsers open (firefox chrome safari internet explorer edge + mobile ones in simulators) all at once on my machine, and they all reported back realtime using Karma + Mocha.

@jordantomax
Copy link

@tobyhinloopen Right you are on all accounts! Thank you!! I'm still a little unclear about why getComputedStyle is necessary, and is it possible to write a setter given that method. For me this seems to work:

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

Thanks again 🙏

@tobyhinloopen
Copy link

@jordantomax getComputedStyle fetches the real computed style for all CSS properties, no matter where it was applied (stylesheet, style attribute or from JS). I'm not sure if this.marginTop does the same, but I guess it does (but only for margin obviously, not for all CSS props)

@jordantomax
Copy link

@tobyhinloopen Ah, got it. Thank you!

@ghost
Copy link

ghost commented Mar 21, 2020

Thank you @tobyhinloopen . I use an even shorter version. I think getComputedStyle is not really needed since I personally never add any css to my unit tests unless it's inline, which is why .style is good enough and faster.

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

@PetrosKalafatidis
Copy link

PetrosKalafatidis commented Oct 17, 2020

Has this been fixed, I am getting 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

@tobyhinloopen
Copy link

tobyhinloopen commented Oct 17, 2020

Has this been fixed, I am getting offsetWidth = 0

@petran it's not realistic to expect jsdom the calculate the width of your SPAN, since it requires knowledge of the size of your font and all inherited CSS properties. I don't think that's the goal of JSDOM. If you want a more accurate simulation of a browser, consider using an actual browser.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
css feature layout Blocked on implementing a layout engine needs tests
Projects
None yet
Development

No branches or pull requests