使用 Javascript 将 src 属性写为 data-src,因为页面正在加载

Use Javascript to write src attribute as data-src, as page is loading

Background: Back in December I wanted my (custom) PHP page-building engine to build a page one-way if the User Agent (UA) could handle Javascript and a different way if the UA could not. To achieve this, I needed to learn:

How to detect UA Javascript capability via PHP, prior to PHP building the page on-the-fly: Detecting javascript in PHP, before PHP builds page

Thanks to some very helpful guidance from @skobaljic, @Abhi Beckert and @GolezTrol, I learned that we can use Javascript to place a session cookie which PHP will be able to query on all subsequent page loads. Consequently, for every page after the first one, PHP will know whether the UA is Javascript-capable or not

So far, so good. Now I have reached the stage where I am no longer building pages with PHP on-the-fly but pre-generating them and uploading them to the server as static HTML documents.

If you're still with me, you'll have guessed correctly this means I am currently needing to pre-generate two static documents for each page - one for the non-JS-capable UAs and one for the JS-capable UAs. This isn't really ideal, hence my question below.

从表面上看,这应该是一个简单的问题。

对于 Javascript-不支持的 UA,我想构建一个包含如下行的文档:

<img src="/myfolder/myimage.png" alt="My Image" />

对于 Javascript 可用的 UA,我想构建包含如下行的相同文档:

<img data-src="/myfolder/myimage.png" alt="My Image" />

(对于那些好奇的人,onload 之后的 javascript 将所有 data-src 属性转换为 src 属性,随后进行了数十次服务器往返但是 - 至关重要的是 - 仅在 页面已经充分加载之后)。

我已经通过 PHP 和一个会话 cookie(参见上面的 背景)实现了这一点,但是现在,如果可能的话,我想执行整个过程在前端。

如何将属性写入 DOM 中的 <img /> 元素默认为 src 但是,另外,(如果 javascript 可用)前缀带有 data- 的属性,使其显示为 data-src...

...这样...

[重要的一点]

如果 UA 是javascript-capable 它将自动并立即写入src 的所有实例因为 data-src 因为页面正在被 UA 下载 ,所以在 onload 之后 javascript 可以重新写入 data-src 又是 src

像这样 - 未经测试但应该可以工作:

var imgs = document.getElementsByTagName('img');

for(var i = 0; i < imgs.length; i++) {
  var currentSrc = imgs[i].getAttribute('src');
  imgs[i].setAttribute('src',''); // remove old src data 
  imgs[i].setAttribute('data-src','currentSrc');
}

所有这一切都是添加一个新的 data-src 属性,并清除当前的 src(如果需要)

</body> 标签之前添加

这是一种 hacky 方式,但所有浏览器 (ie6+) 都支持它:

<body>

    <noscript>
         <style>.img-hide { display: none; }</style>
    </noscript>

    <noscript><img src="/path/to/image1.jpg"></noscript>
    <img class="img-hide" data-src="/path/to/image1.jpg">

    <noscript><img src="/path/to/image2.jpg"></noscript>
    <img class="img-hide" data-src="/path/to/image2.jpg">

    <!-- etc etc etc -->

</body>

经过将近3天的head-scratching,终于破解了

头部

<head>

<script>
function initialiseImages() {
        var scripts = document.getElementsByTagName('script');
        var section = scripts[scripts.length-1].parentNode;
        var images = section.getElementsByTagName('img');
        for (var i = 0; i < images.length; i++) {
                        var src = images[i].getAttribute('src');
                        var datasrc = document.createAttribute('data-src');
                        datasrc.value = src;
                        images[i].setAttributeNode(datasrc);
                        images[i].removeAttribute('src');}}
</script>

</head>

BODY

<body>

<section>
<h2><img src="/myfolder/myimage.png" alt="My Image" />Second Level Heading</h2>
<ul><li><img src="/myfolder/myimage.png" alt="My Image" /></li></ul>
<script>initialiseImages();</script>
</section>

<section>
<h3>Third Level Heading</h3>
<ul>
<li><img src="/myfolder/myimage.png" alt="My Image" /></li>
<li><img src="/myfolder/myimage.png" alt="My Image" /></li>
<li><img src="/myfolder/myimage.png" alt="My Image" /></li>
</ul>
<script>initialiseImages();</script>
</section>

</body>

更新 1

感谢 Dave Walsh 的文章引用脚本自己的标签 (http://davidwalsh.name/script-tag),我 re-written function initialiseImages() 的前 3 行:

    var scripts = document.getElementsByTagName('script');
    var section = scripts[scripts.length-1].parentNode;
    var images = section.getElementsByTagName('img');

单行:

var images = document.currentScript.parentNode.getElementsByTagName('img');

据我所知,这定义了 var images 稍微快一点,因此在 UA 向服务器发送 file-request 之前捕获了更多 <img src= 的实例。


开发人员注释如下...

尝试 #1:尝试让 Javascript 表现得像 PHP

相对于部署Javascript,我更熟悉在PHP中编写页面on-the-fly,所以我的初衷是想办法使用Javascript以 quasi-server-side 的方式 re-write DOM 在它到达的那一刻瞬间:

For Javascript-non-capable UAs, I want to build a document which contains lines like this:

<img src="/myfolder/myimage.png" alt="My Image" />

For Javascript capable UAs, I want to build the same document which contains lines like this:

<img data-src="/myfolder/myimage.png" alt="My Image" />

如果这样的方法可行,我找不到实现它的方法 - 而且,公平地说,Javascript 当然不是为瞬时 re-writing 元素属性设计的他们到了。

尝试 #2:通过 Javascript-added CSS

阻止图像下载

我的第二种方法是看看我是否可以通过 CSS.

行来阻止图像下载

如果我用 Javascript 添加该行,则只有 Javascript-capable 个 UA 能够读取 image-blocking 样式规则。

这也没有用。事实证明,您可以直接阻止 CSS background-images 下载,方法是将 display:none; 应用于元素的 parent 容器 background-image。但是我想要阻止的图像(直到加载之后)具体是 <img>s,而不是 background-images - 事实证明,一旦 UA 命中 <img src,它就会跑到服务器下载图片。

尝试 #3:在每个 <img>

中内联 Javascript

内联 Javascript 很漂亮 old-school,但@Darren Sweeney 已经提出了一个富有想象力的 <noscript> 解决方案,所以我不想自动忽略 old-school。我想像这样解决这个问题:

<img src="/myfolder/myimage.png" alt="My Image" onEvent="initialiseImage();" />

我 运行 进入这里的主要问题是我找不到任何合适的 onEvent。我将 initialiseImage() 函数放在 <head> 中,然后我希望每次 UA 达到 <img> 时自动触发该函数 - 但经过广泛阅读后,我不确定自动内联执行是可能的。 (也许有人可以在下面的评论中让我知道,如果是...)

当然,我找不到 onParse

唯一有意义的 element-specific onEventonLoad,在 <img> 的情况下(如果我理解正确的话)下载 <img> 在它触发之前。所以那一点都不好。

尝试 #4:终于成功了!

到目前为止:

1) 从一开始,问题就是等到 window.onload 才使用 Javascript 重写每个 <img src= 属性太晚了;

2) 我找不到使用 Javascript 在下载时立即重写每个 <img src= 的方法;

3) 在 UA 解析 <img>;

时,我看不到使用 Javascript 重写每个 <img src= 属性的方法

我推断也许我仍然可以使用 Javascript 重写每个 <img src= 属性,此时 UA 完成解析每个 <section> 包含每组 <img>s

我很高兴地说我已经相当彻底地测试了上面的代码并且它似乎(到目前为止)工作完美 - 早在 UA 开始从服务器请求 <img> 文件之前,<img src= 属性已替换为 <img data-src= 属性 并且不再执行该请求。