1904labs / dom-to-image-more

Generates an image from a DOM node using HTML5 canvas

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Use full hierarchy when determining the browser default styles

IDisposable opened this issue · comments

Use case: description, code

Per the original reported problem of #95, and the discoveries made while moving towards a solution in #104, we know that the only reasonable way to get the full defaults of a particular element is to generate a matching DOM element hierarchy in the sandbox such that (for example) a TD in a TR in a TBODY in a TABLE in a DIV in BODY would need all the intervening elements created in the sandbox and then we could insert the element we need now to determine its in-situ default styles.

Expected behavior

Insert a TD and see the default 1px style that would have been driven by the fact that it's nested in a TABLE with a TBODY with a TR

Actual behavior (stack traces, console logs etc.)

On Chrome, they default style of a bare TD inserted as a direct descendent of BODY lies and says that its padding is 0px instead of what would actually be a 1px padding due to the table-cell display.

Library version

2.13.1 and 3.0.0

Browsers

  • Chrome 49+
  • Firefox 45+

The proposed solution is mentioned in #104 (comment)
and basically means we need to create a matching root-to-child hierarchy in the sandbox. Because this is kind of complex, we might want to leave the sandbox "live" longer, but that will need some validation.

commented

I think we should be limiting that effort to reach up.

Maybe we could chunk the element's prototype name and see if the element is "similar" to its parent:

  • get the prototype name of each element (Object.getPrototypeOf(...).constructor.name)
  • get the first word and remove it from the string - it can either be HTML SVG or /^[A-Z][^A-Z]*/
  • get the second word - it'll be HTML SVG or /^[A-Z][^A-Z]*/ again.
    (For any table elements and ruby elements it'll be HTML, then Table or Element - we can exploit that!)

Is that better than just doing a concatenation of the tagName of each element going up to the first thing that has a display: block or display: table as the default display style? This would result in TD.TR.TBODY.TABLE (stopped by TABLE's default style of table) or IMG.SPAN.P.DIV (stopped by DIV's default style of block)... or even just white-list the elements based on the CSS default values spec... then the cached default styles would be keyed by the resulting string.

commented

It wouldn't work for the RT tag in RUBY, which is rendered as block and CSS hasn't implemented yet (so another styling pice owned by Layout in Blink):

ポケット(ポケ)モンスター(モン) (see the ruby > rt style declaring display: block here 😩)

The W3Schools list is also missing SVG elements which is an avenue of weirdness all unto its own.

My solution wouldn't work for <ul> and <li> FWIW.

Your suggestion would be a start. I personally think we could cache default styles without accounting for hierarchy e.g TD.TR.TBODY.TABLE getting cached as TD

I'll admit that I never really attempted to target RUBY so I've got full-on-ignorance about the topic... I'll dig some into that and likely spin up a different issue for what I grok.

In the meantime, I'm going to make a trial to see if I can resolve the original issue this way, and then optimize down to the simplest solution that works. :)

Looks like the best canonical list of stop tags is Block Level elements... for RUBY looks like we should just treat them like any other Phrasing content and keep crawling up...

commented

That list, if we add <svg>, is something we could hardcode fairly. This means getDefaultStyle will not reach up from body head and html also. Which is okay.

POC works! See https://jsfiddle.net/ae1m6b5v/4/ (which has the updated source embedded)