重新加载页面时,加载事件未在Safari上触发

时间:2016-01-08 12:54:26

标签: javascript html5 svg onload-event object-tag

我在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');

    ...
}

2 个答案:

答案 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.htmlrenderer.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;
}
...