我在object
标记内加载了一个简单的SVG,如下面的代码所示。在Safari上,当我在打开浏览器后第一次加载页面时,load
事件只被触发一次。所有其他时间它没有。我正在使用load
事件用GSAP初始化一些动画,因此我需要知道SVG何时完全加载才能选择DOM节点。似乎有效的快速解决方法是使用setTimeout
而不是附加到事件,但似乎有点麻烦,因为较慢的网络无法在指定的时间内加载object
。我知道这个事件并不是真正的标准化,但我认为我不是第一个遇到这个问题的人。你会如何解决它?
var myElement = document.getElementById('my-element').getElementsByTagName('object')[0];
myElement.addEventListener('load', function () {
var svgDocument = this.contentDocument;
var myNode = svgDocument.getElementById('my-node');
...
}
答案 0 :(得分:3)
听起来更像是问题在于,当缓存数据时,加载事件会在>>附加处理程序之前触发。
您可以尝试在附加事件后重置数据属性:
object.addEventListener('load', onload_handler);
// reset the data attribte so the load event fires again if it was cached
object.data = object.data;
答案 1 :(得分:1)
在开发电子应用程序时,我也遇到了这个问题。在我的工作流程中,我用VSCode编辑index.html
和renderer.js
,然后按<Ctrl>+R
来查看更改。我仅重新启动调试器以捕获对main.js
文件所做的更改。
我想加载一个SVG,然后可以从我的应用程序中对其进行操作。因为SVG很大,所以我更喜欢将其保存在从磁盘加载的外部文件中。为此,HTML文件index.html
包含以下声明:
<object id="svgObj" type="image/svg+xml" data="images/file.svg"></object>
renderer.js
中的应用程序逻辑包含:
let svgDOM // global to access SVG DOM from other functions
const svgObj = document.getElementById('svgObj')
svgObj.onload = function () {
svgDOM = this.contentDocument
mySvgReady(this)
}
该问题不是显而易见的,因为它看起来是断断续续的:当调试器/应用程序第一次启动时,它可以正常工作。但是,当通过<Ctrl>+R
重新加载应用程序时,.contentDocument
属性为null
。
经过大量调查和梳理后,有关此问题的一些长篇注释包括:
svgObj.addEventListener ('load', function() {...})
代替
svgObj.onload
没有区别。使用addEventListener
更好,因为尝试通过“ onload”设置另一个处理程序
将替换当前的处理程序。与其他Node.js
相反
应用程序,则不需要removeEventListener
已从DOM中删除。 IE的旧版本(11之前的版本)有问题,但
现在应该认为这是安全的(无论如何不适用于Electron)。this.contentDocument
。有一个更好看的
getSVGDocument()
方法有效,但这似乎是向后的
与旧版Adobe工具(可能是Flash)的兼容性。返回的DOM是相同的。按照@Kaiido的描述,一旦加载SVG DOM似乎将被永久缓存,除了我相信从不会触发该事件。更重要的是,在Node.js
中,SVG DOM仍保留在已加载的相同svgDOM
变量中。我一点都不明白。我的直觉表明,require('renderer.js')
中的index.html
代码已将其缓存在模块系统中的某个位置,但是对renderer.js的更改确实生效,因此这并不是全部答案。
无论如何,这里有一种替代方法可以在Electron的渲染过程中捕获对我有用的SVG DOM:
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)
}
接下来,我很失望地得知index.html
加载的CSS规则无法访问SVG DOM中的元素。有多种方法可以通过编程方式将样式表注入SVG DOM,但最终我将index.html
更改为以下格式:
<svg id="svgObj" class="svgLoader" src="images/file.svg"></svg>
然后我将此代码添加到renderer.js
中的DOM设置代码中,以将SVG直接加载到文档中。如果您使用的是压缩的SVG格式,我希望您需要自己进行解压缩。
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
})
...
})
我不一定喜欢它,但这是我要使用的解决方案,因为我现在可以更改SVG对象上的类,并且我的CSS规则适用于SVG中的元素。例如,现在可以使用index.css
中的这些规则来声明性地更改显示SVG的哪些部分:
...
#svgObj.cssClassBad #groupBad,
#svgObj.cssClassGood #groupGood {
visibility: visible;
}
...