admin管理员组文章数量:1399887
I have a simple SVG loaded inside a object
tag like the code below. On Safari, the load
event is fired just once, when I load the first time the page after opening the browser. All the other times it doesn't. I'm using the load
event to initialize some animations with GSAP, so I need to know when the SVG is fully loaded before being able to select the DOM nodes. A quick workaround that seems to work is by using setTimeout
instead of attaching to the event, but it seems a bit akward as slower networks could not have load the object
in the specified amount of time. I know this event is not really standardized, but I don't think I'm the first person that faced this problem. How would you solve it?
var myElement = document.getElementById('my-element').getElementsByTagName('object')[0];
myElement.addEventListener('load', function () {
var svgDocument = this.contentDocument;
var myNode = svgDocument.getElementById('my-node');
...
}
I have a simple SVG loaded inside a object
tag like the code below. On Safari, the load
event is fired just once, when I load the first time the page after opening the browser. All the other times it doesn't. I'm using the load
event to initialize some animations with GSAP, so I need to know when the SVG is fully loaded before being able to select the DOM nodes. A quick workaround that seems to work is by using setTimeout
instead of attaching to the event, but it seems a bit akward as slower networks could not have load the object
in the specified amount of time. I know this event is not really standardized, but I don't think I'm the first person that faced this problem. How would you solve it?
var myElement = document.getElementById('my-element').getElementsByTagName('object')[0];
myElement.addEventListener('load', function () {
var svgDocument = this.contentDocument;
var myNode = svgDocument.getElementById('my-node');
...
}
Share
Improve this question
asked Jan 8, 2016 at 12:54
StefanoStefano
3,25810 gold badges62 silver badges101 bronze badges
1
- This is a great answer to a more specific question. It would be nice if the title specifically referenced problems loading an SVG DOM within an <object> tag, and the .contentDocument property returning null. – rand'Chris Commented Apr 24, 2020 at 15:19
2 Answers
Reset to default 4It sounds more like the problem is that, when the data is cached, the load event fires before you attached the handler.
What you can try is to reset the data attribute once you attached the event :
object.addEventListener('load', onload_handler);
// reset the data attribte so the load event fires again if it was cached
object.data = object.data;
I also ran into this problem while developing an Electron application. In my workflow I edit index.html
and renderer.js
in VSCode, and hit <Ctrl>+R
to see the changes. I only restart the debugger to capture changes made to the main.js
file.
I want to load an SVG that I can then manipulate from my application. Because the SVG is large I prefer to keep it in an external file that gets loaded from disk. To acplish this, the HTML file index.html
contains this declaration:
<object id="svgObj" type="image/svg+xml" data="images/file.svg"></object>
The application logic in renderer.js
contains:
let svgDOM // global to access SVG DOM from other functions
const svgObj = document.getElementById('svgObj')
svgObj.onload = function () {
svgDOM = this.contentDocument
mySvgReady(this)
}
The problem is non-obvious because it appears intermittent: When the debugger/application first starts this works fine. But when reloading the application via <Ctrl>+R
, the .contentDocument
property is null
.
After much investigation and hair-pulling, a few long-form notes about this include:
- Using
svgObj.addEventListener ('load', function() {...})
instead ofsvgObj.onload
makes no difference. UsingaddEventListener
is better because attempting to set another handler via 'onload' will replace the current handler. Contrary to otherNode.js
applications, you do not need toremoveEventListener
when the element is removed from the DOM. Old versions of IE (pre-11) had problems but this should now be considered safe (and doesn't apply to Electron anyway). - Usage of
this.contentDocument
is preferred. There is a nicer-lookinggetSVGDocument()
method that works, but this appears to be for backwards patibility with old Adobe tools, perhaps Flash. The DOM returned is the same.
The SVG DOM appears to be permanently cached once loaded as described by @Kaiido, except that I believe the event never fires. What's more, in Node.js
, the SVG DOM remains cached in the same svgDOM
variable it was loaded into. I don't understand this at all. My intuition suggests that the require('renderer.js')
code in index.html
has cached this in the module system somewhere, but changes to renderer.js do take effect so this can't be the whole answer.
Regardless, here is an alternate approach to capturing the SVG DOM in Electron's render process that is working for me:
let svgDOM // global to access from other functions
const svgObj = document.getElementById('svgObj')
svgObj.onload = function () {
if (svgDOM) return mySvgReady(this) // Done: it already loaded, somehow
if (!this.contentDocument) { // Event fired before DOM loaded
const oldDataUri = svgObj.data // Save the original "data" attribute
svgObj.data = '' // Force it to a different "data" value
// setImmediate() is too quick and this handler can get called many
// times as the data value bounces between '' and the actual SVG data.
// 50ms was chosen and seemed to work, and no other values were tested.
setTimeout (x => svgObj.data = oldDataUri, 50)
return;
}
svgDOM = this.contentDocument
mySvgReady(this)
}
Next, I was very disappointed to learn that the CSS rules loaded by index.html
can't access the elements within the SVG DOM. There are a number of ways to inject the stylesheet into the SVG DOM programmatically, but I ended up changing my index.html
to this format:
<svg id="svgObj" class="svgLoader" src="images/file.svg"></svg>
I then added this code to my DOM setup code in renderer.js
to load the SVG directly into the document. If you are using a pressed SVG format I expect you will need to do the depression yourself.
const fs = require ('fs') // This is Electron/Node. Browsers need XHR, etc.
document.addEventListener('DOMContentLoaded', function() {
...
document.querySelectorAll ('svg.svgLoader').forEach (el => {
const src = el.getAttribute ('src')
if (!src) throw "SVGLoader Element missing src"
const svgSrc = fs.readFileSync (src)
el.innerHTML = svgSrc
})
...
})
I don't necessarily love it, but this is the solution I'm going with because I can now change classes on the SVG object and my CSS rules apply to the elements within the SVG. For example, these rules from index.css
can now be used to declaritively alter which parts of the SVG are displayed:
...
#svgObj.cssClassBad #groupBad,
#svgObj.cssClassGood #groupGood {
visibility: visible;
}
...
本文标签: javascriptLoad event not fired on Safari when reloading pageStack Overflow
版权声明:本文标题:javascript - Load event not fired on Safari when reloading page - Stack Overflow 内容由网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://www.betaflare.com/web/1744211735a2595449.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论