Currently, the .naturalWidth and .naturalHeight attributes of HTMLImageElement take on the value 0 when the image doesn't have an intrinsic width/height. This is unfortunate, because images can actually have a zero intrinsic width/height, and you can't tell the difference between these two states. (It's also just generally a bad practice to use a sentinel value like this.)
For Typed OM level 2, when I expose the intrinsic sizes of images, I want to mostly match up with HTML, but am planning on making the attributes nullable instead, so that a number indicates the actual intrinsic size, and null means no size in that axis. CSS has a lot more stretchy images.
In https://github.com/w3c/css-houdini-drafts/issues/714#issuecomment-368219765, @annevk wonders if we could make this change for HTML as well.
This is about naturalWidth and naturalHeight, right? width and height represent the rendered size.
Sorry, yeah. Will amend.
Here's an informal test case for exploring current browser behavior:
https://codepen.io/AmeliaBR/pen/gvEJWr/
It prints to the console the naturalWidth and naturalHeight of four SVGs:
viewBox (defined aspect ratio) but no dimensions (height or width in the SVG file)viewBoxCurrent browser behavior:
Firefox (v59 tested): images with no intrinsic dimensions always report 0脳0 natural sizes, regardless of whether there is an intrinsic aspect ratio (AKA, an SVG viewBox); the SVG with defined size is reported as expected.
Chrome (v64) always reports a naturalHeight of 150 for the SVGs with no intrinsic size, and reports a naturalWidth that is based on that plus the aspect ratio, or 300 if there is no aspect ratio.
Edge (v16) always reports a naturalWidth of 300 for the SVGs with no intrinsic size, and reports a naturalHeight that is based on that plus the aspect ratio, or 150 if there is no aspect ratio.
Safari (v11) reported the current displayed sizes the first time I ran it (aka, the SVGs with viewBox but no dimensions were reporting a naturalWidth based on the frame width), but then when I changed the frame width & re-ran the code, it reported the width from the viewBox of each file (not sure what was going on there)
So, in summary: Yeah, a spec describing the expected result would be nice to have.
The IDL attributes naturalWidth and naturalHeight must return the density-corrected intrinsic width and height of the image, in CSS pixels, if the image has intrinsic dimensions and is available, or else 0.
The words "intrinsic dimensions" are linked to a definition in CSS 2:
Intrinsic dimensions
The width and height as defined by the element itself, not imposed by the surroundings. CSS does not define how the intrinsic dimensions are found. In CSS 2 only replaced elements can come with intrinsic dimensions. For raster images without reliable resolution information, a size of 1 px unit per image source pixel must be assumed.
That section of CSS has been replaced/clarified by a much longer section in CSS Images 3, which defines "intrinsic dimensions" as (in part):
the set of the intrinsic height, intrinsic width, and intrinsic aspect ratio (the ratio between the width and height), each of which may or may not exist for a given object.
So the term "intrinsic dimensions" now includes a defined aspect ratio, regardless of whether or not there is a defined size. Which makes the correct HTML behavior as undefined as the browser test results suggest.
I will say I prefer an option that allows you to calculate a valid aspect ratio for images that have an intrinsic aspect ratio (0,0 is not so useful for that). But by that same measure, it is misleading to report a fixed aspect ratio for a completely dimensionless image. It would also be useful to distinguish fixed dimensions (whether 0 or anything else) from flexible ones.
Maybe we need a .naturalAspectRatio IDL property? And then naturalWidth and naturalHeight could report null (or 0, if needed for legacy compat) when they are undefined, while still making the aspect ratio available?
null and Infinity are the only values that make sense to me as a developer.
undefined typically implies a lack of browser support.
null otoh is an explicit way to say that there isn't a value for a property.
@tleverett I agree, and just changed my comment to suggest null instead of undefined. Another option would be NaN, probably better than Infinity. But null probably makes the most sense (and was what @tabatkins suggested initially).
Thanks @AmeliaBR, that's great.
I have a feeling we might be able to convince implementers to fix these attributes given that SVG support is relatively recent and probably does not break compatibility to change the return values there. I also agree we should add the missing aspect ratio attribute. (And we should maybe not define intrinsic in terms of CSS here as it comes from the image, not an element.)
I'd be happy to help campaign getting this sorted. @jakearchibald is probably too.
(And we should maybe not define intrinsic in terms of CSS here as it comes from the image, not an element.)
I think the current CSS text (in CSS Images Level 3/4) is applicable. It applies to all image types, not just embedded images, and is defined as a property of the image itself, not the embedding image. That said, it doesn't explicitly define how the various measurements (intrinsic height, width, and aspect ratio) are derived for individual image types, that's left up to the image format. For SVG, it's defined in the SVG spec.
Maybe I'm thinking about this wrong, forgive me if I missed the point.
My expectation and preference would be for the rendered size to match first the stated width and height, if the <svg> element has those, and failing that, to use the width and height from the viewBox attribute, in pixels.
For example,
<svg viewBox="0 0 10 50">: width=10px, height=50px<svg viewBox="-5 30 2524 113">: width=2524px, height=113px(I know that's not what the CSS spec says, but that default is never what I want.)
This would be intuitive, predictable, easy to adjust as needed (by changing the viewBox values or setting an explicit width or height in SVG or CSS), and would be derived from the SVG file itself (in the same way that a raster's width and height are derived from that file's metadata). It also honors the SVG author's intent regarding the aspect ratio and dimensions.
Seeing how SVG icons are commonly being used today, this seems like it would be the best default, requiring the least thought and effort on behalf of the content author.
I think, by extension, the .naturalWidth and .naturalHeight would match those default rendering dimensions.
As much as I'd personally prefer intrinsic dimensions that match the viewBox dimensions, I have code in the wild (and given to others) that optimizes SVGs by bumping up viewBox coordinate place-values, adding transform="scale(10)" to sub-elements, then runs the SVG through svgo to bake in the transforms.
It's only one dev's small bit of code, but I suspect other CSS/etc. in the wild expects svg[viewBox] = width: 100%, too.
Changing the default sizing behavior of embedded SVG isn't an option at this point. It has taken us far too long to get cross-browser compatibility on the current behavior. If you want a default size for your SVG, adding width and height attributes is a simple way to do it.
For the subject of this issue:
Exposing the viewBox width and height of an embedded SVG image via naturalWidth and naturalHeight is a possible option (at least some of the time, it's what Safari does). But I see three problems with it:
viewBox dimensions can be decimal numbers; the DOM properties are integers.viewBox and explicit width and height (like <svg viewBox="0 0 16 8" width="200px" height="100px" />).naturalWidth/Height are intended to reflect the natural width and height of the image - what size it'll assume with no other sizing constraints placed on it. Absent a change to how browsers size SVG images, there is no way that viewBox should have any effect on these attributes. (Until we add naturalRatio, at which point it sometimes defines that attribute.)
In the past I've used naturalWidth/Height for scaling images down, or padding images up while retaining aspect ratio.
It wouldn't make sense to use the viewBox values as they would imply the image couldn't be scaled up.
This is where a value of Infinity starts to make sense.
if (newWidth < img.naturalWidth) {
...resize via aspect ratio...
} else {
...pad via aspect ratio...
}
For this to work, naturalAspectRatio would be required.
If the actual width and height are not available, then I expect the vast majority of people will at least be grateful to get some information about the aspect ratio of the image. Then at least, they will have an idea how much they need to scale the image, or screen space they need to reserve. So I think returning values that allow a reasonable accurate calculation would be the most useful.
One other problem with returning viewBox width and height, is that they can be very small, fractional, values. Rounding those values up or down won't produce useful results. Imagine a viewBox of "0 0 2 1.5". Here a naturalHeight of either 1 or 2, is likely not that helpful.
Multiplying by amounts derived from the standard 300x150 replaced content box, seems like a good compromise. Edge's use of the larger 300 value should allow the best accuracy in general. And if the values are used directly as-is in a page, then at least the SVG will be sized large enough to be visible and useful.
One thing we have to be careful about here is that for no-cors cross-origin vector images we cannot expose the aspect ratio (unless it has an explicit width and height). Otherwise we'd start leaking more data. Hmm, or maybe you can already determine it by setting a width and then observing the height.
maybe you can already determine it by setting a width and then observing the height.
You can, so I don't think there is any additional CORS security risk with adding a naturalAspectRatio property.
(I mean, if we were creating this API today, we wouldn't even let the width & height be query-able, or affect layout, for cross-origin images. Because it does mean that the embedding website can detect the difference between a successful and unsuccessful image fetch. But that ship has sailed long, long ago.)
I wonder if it's reasonable that we continue to return 0 while the image is not yet available. It seems like changing it to return null there would be a bigger risk.
濡傛灉瀹冭繑鍥炵殑鍊肩洿鎺ユ垨鑰呴棿鎺ョ殑杩斿洖浜唘ndefined,鑰岀煝閲忓浘鍦ㄦ病鏈夎杩涜缂╂斁鐨勬儏鍐典笅,閭e氨姣棤鎰忎箟
Firefox is starting to encounter Web compat problems due to our different handling of naturalWidth/naturalHeight on SVG images with no intrinsic size. It causes GitHub previews of SVG images with no width/height to be presented with a zero width. Is there any reason for us not to change to match what Chrome is doing currently?
So Chrome uses the 150/300 defaults from CSS, which seems buggy, but okay. (One case @AmeliaBR hasn't tested it seems is defining either a width or height. I guess in those cases the aspect ratio is used (if there is one).)
So what to define:
And then there's a separate question whether to still support the unknown state from script.
This causes issues when checking if image is loaded using img.complete && img.naturalWidth!=0 on Firefox that reports _naturalWidth_ as _0_ for loaded SVG.
If using javascript to attach _error/load_ listener, img can be already loaded with error but cannot check it.
So there is no way to check if the svg image is loaded without inlining onerror (or onload) on img html element to catch it.
https://stackoverflow.com/questions/1977871/check-if-an-image-is-loaded-no-errors-with-jquery
One case @AmeliaBR hasn't tested it seems is defining either a width or height. I guess in those cases the aspect ratio is used
I added some of those cases to the examples. Yet again, a discrepancy between Chromium and Firefox. Firefox ignores the viewBox that is used for scaling the actual image size, and reports 0 for the auto width/height. Chrome uses the viewBox so that the natural image + height always have the correct aspect ratio. In both cases, percentage dimension is treated the same as auto when it comes to intrinsic dimensions (which is per spec).
Most helpful comment
Here's an informal test case for exploring current browser behavior:
https://codepen.io/AmeliaBR/pen/gvEJWr/
It prints to the console the
naturalWidthandnaturalHeightof four SVGs:viewBox(defined aspect ratio) but no dimensions (heightorwidthin the SVG file)viewBoxCurrent browser behavior:
Firefox (v59 tested): images with no intrinsic dimensions always report 0脳0 natural sizes, regardless of whether there is an intrinsic aspect ratio (AKA, an SVG
viewBox); the SVG with defined size is reported as expected.Chrome (v64) always reports a
naturalHeightof 150 for the SVGs with no intrinsic size, and reports anaturalWidththat is based on that plus the aspect ratio, or 300 if there is no aspect ratio.Edge (v16) always reports a
naturalWidthof 300 for the SVGs with no intrinsic size, and reports anaturalHeightthat is based on that plus the aspect ratio, or 150 if there is no aspect ratio.Safari (v11) reported the current displayed sizes the first time I ran it (aka, the SVGs with
viewBoxbut no dimensions were reporting anaturalWidthbased on the frame width), but then when I changed the frame width & re-ran the code, it reported the width from theviewBoxof each file (not sure what was going on there)So, in summary: Yeah, a spec describing the expected result would be nice to have.
Current spec text:
The words "intrinsic dimensions" are linked to a definition in CSS 2:
That section of CSS has been replaced/clarified by a much longer section in CSS Images 3, which defines "intrinsic dimensions" as (in part):
So the term "intrinsic dimensions" now includes a defined aspect ratio, regardless of whether or not there is a defined size. Which makes the correct HTML behavior as undefined as the browser test results suggest.
I will say I prefer an option that allows you to calculate a valid aspect ratio for images that have an intrinsic aspect ratio (0,0 is not so useful for that). But by that same measure, it is misleading to report a fixed aspect ratio for a completely dimensionless image. It would also be useful to distinguish fixed dimensions (whether 0 or anything else) from flexible ones.
Maybe we need a
.naturalAspectRatioIDL property? And thennaturalWidthandnaturalHeightcould reportnull(or 0, if needed for legacy compat) when they are undefined, while still making the aspect ratio available?