Open banksJeremy opened 12 years ago
Observed difference between element in .svg
and element in imported .html
:
<image />
tag ends up being converted to an <image></image>
tag.xlink:href
attribute is changed to href
.Manually changing these back didn't fix it.
When I view the element in the document inspector the href
property is always collapsed in the .HTML, but displayed in-full in the .SVG. The actual value is the same, but it's possible that this hints at a difference.
It seems link xlink:href
might be the problem...
Using href
, image not visible: http://jsfiddle.net/jeremy/qHEWG/
Using xlink:href
, image visible: http://jsfiddle.net/jeremy/qHEWG/1/
It looks like this is caused by converting the SVG it to source and then re-parsing it. Chrome strips the namespaces when I do this, and henceforth ignores the namespaces when I set new attributes.
Given that SVGs are explicitly mentioned in the HTML fragment serialization algorithm in HTML5, you would expect them to round-trip without doing something stupid like this.
Firefox does not have this problem.
I made a nice little test case to demonstrate the problem and stumbled upon a stupid, hacky but working solution. Before re-parsing, do this to each image element:
var href = imageEl.getAttribute( "xlink:href" );
imageEl.removeAttribute( "xlink:href" );
imageEl.setAttribute( "xlink:href", href );
...yup.
(Aside: I realized that I'm not supposed to expect modifying attributes in the parsed document to have an effect, it just happens to do so for some attributes. Chrome not reflecting these changes was not a bug.)
I realized that this issue also occurs a second time when we're exporting the finished document through Butter, so we need to "fix" the attributes again after they're reparsed.
I wanted to make a function that would find any elements with xlink:href
attributes and fix them. Unfortunately, the spiffy new querySelector methods aren't namespace-friendly so I've been mucking around with Xpath.
Apparently Xpath usually won't return results for an unrooted tree, making this even more of a pain to deal with. Maybe I'll create an XML/SVG document in-memory and use that? I should experiment with this; maybe inserting into an SVG document first and then moving the nodes over to an HTML document will make things behave better...
edit: forget about trying to be general and perfect and actually understanding what's going on. I'll just use our existing code. For the record, here's what I was trying:
function fixSvgHrefs( root ) {
function nsResolver( prefix ) {
return prefix == "xlink" ? "http://www.w3.org/1999/xlink"
: prefix == "svg" ? "http://www.w3.org/2000/svg"
: null;
}
var iter = document.evaluate( '//svg:svg//*[@href]', root || document, nsResolver, XPathResult.UNORDERED_NODE_ITERATOR_TYPE ),
el;
while( el = iter.iterateNext() ) {
var href = el.getAttribute( "href" );
el.removeAttribute( "href" );
el.setAttribute( "xlink:href", href );
}
}
I made a test page to evaluate this behaviour.
When parsing an xlink:href
attribute of an <image>
element in an embedded <svg>
, Chrome and Firefox both create an attribute named xlink:href
in the namespace http://www.w3.org/1999/xlink
with the localName href
.
When converting to HTML, Firefox uses the full name, xlink:href=
. However, Chrome uses the localName href=
. When parsed again, this produces an href
attribute without a namespace, which works in neither browsers.
If you create an attribute with no namespace and a localName of xlink:href
, Chrome will use it but Firefox will not.
Chrome requires an element with a full name of xlink:href
to display correctly.
Chrome requires an element with a localName of xlink:href
to serialize correctly.
Firefox requires an element with a localName of href
in the xlink namespace to display and serialize correctly.
This explains why removing and re-adding the attribute using the namespace-insensitive methods fixed things in Chrome, but broke them in Firefox.
Since Chrome and Firefox require an attribute of the same name but with different properties there may be no general solution; feature/browser detection may be required.
Ugly, but not too much of a pain:
var tmp = document.createElement( "div" )
tmp.innerHTML = "<svg><image xlink:href=\"about:blank\"></image></svg>";
var dropsXlinkNamespace = !/xlink:href/.test( tmp.innerHTML );
I'm still not entirely sure what's going on, but this is now working in Chrome and Firefox, including exported presentations and without breaking text selection.
Embedded images often don't show up. I'm not sure why. They are embedded in the document (I can load the data: url and see the picture), and they seem to be in the slide at the correct location. If I hover over their element in the debug tools, it will highlight a region in the middle of the slide, but nothing will be visible there.
If I open the SVG directly in the browser the image will be visible, so this is probably my fault.
Figure this out.