预加载CSS图片

141

我有一个隐藏的联系表单,点击按钮后会展开。它的字段被设置为CSS背景图像,并且它们总是比已切换的div稍晚出现。

我曾经在部分中使用这个代码片段,但是没有成功(即使我清除了缓存):

<script>
$(document).ready(function() {
        pic = new Image();
        pic2 = new Image();
        pic3 = new Image();
        pic.src="<?php bloginfo('template_directory'); ?>/images/inputs/input1.png";
        pic2.src="<?php bloginfo('template_directory'); ?>/images/inputs/input2.png";
        pic3.src="<?php bloginfo('template_directory'); ?>/images/inputs/input3.png";
});
</script>

我使用jQuery作为我的库,如果我能用它来解决这个问题就太棒了。

谢谢你的建议。


请展示你的代码。你如何切换这个表单? - n1313
1
n1313,我使用了最简单的方法: $ ('#toggle').click(function(){ $('#contact')。slideToggle(); }); - Peanuts
13个回答

318

仅使用CSS预加载图像

在下面的代码中,我随机选择了body元素,因为它是页面上唯一保证存在的元素之一。

为了让这个“技巧”起作用,我们将使用content属性来方便地设置要加载的多个URL,但如所示,::after伪元素被保持隐藏,以便图像不会被渲染:

body::after{
   position:absolute; width:0; height:0; overflow:hidden; z-index:-1; // hide images
   content:url(img1.png) url(img2.png) url(img3.gif) url(img4.jpg);   // load images
}

在这里输入图片描述

演示


如果有很多相对较小的图像,最好使用雪碧图来减少 HTTP 请求,并确保这些图像托管在使用 HTTP2 的服务器上。


4
目前为止最好的技术,因其简单性和纯CSS方法而著称!唯一的缺点是这些图片会与立即可见的内容同时加载,而不是在主要内容加载后延迟加载,这需要使用JavaScript。但除非你要预加载数十张图片,否则这不应该成为一个致命问题。 - BenMorel
2
这对于预加载CSS按钮的悬停资产非常棒,这样当您在资产加载时悬停在按钮上时,就不会出现任何闪烁。 - Robert Petz
21
好的解决方案。我建议不要将其应用于“body”,因为这些图片会加载到引用样式表的每个页面上。相反,您应该使用“.contact_form:after {...}”或其他全局性较小的类。当然,如果您的联系表格在每个页面上都存在的话,除非。 - CloudMagick
1
@CloudMagick - 这里不能做出推荐。每个使用案例都是独特的。我自己在网页上使用它,因为有 SVG 和 GIF 图像,即所谓的“加载动画”,它们可能随时随地出现,因此必须进行跨站点预加载。 - vsync
3
@vsync,这很有道理,特别是对于gif和小型可重用内容。我最近遇到的情况是针对内容丰富的轮播大背景图像,我肯定不希望这些大文件随处加载 :) - CloudMagick
显示剩余6条评论

60

使用 HTML <link> 标签预加载图片

我相信这个问题的大部分访问者都在寻找答案:"如何在页面渲染开始之前预加载一张图片?"而这个问题最好的解决方案是使用<link>标签,因为<link>标签可以阻止页面的进一步渲染。详见 preemptive

这两个rel属性(当前文档与链接文档之间的关系)的值选项与此问题最相关:

  • prefetch:在页面渲染时加载给定资源
  • preload:在页面渲染开始之前加载给定资源

所以,如果您想在<body>标签的渲染过程开始之前加载资源(在本例中为图片),请使用:

<link rel="preload" as="image" href="IMAGE_URL">

如果您想在渲染<body>时加载资源,但计划稍后动态使用它并且不想打扰用户使用它的时间,请使用:

<link rel="prefetch" href="RESOURCE_URL">

值得注意的是,Mozilla浏览器引擎只会在浏览器处于空闲状态时加载“prefetch”,这由“nsIWebProgressListener”API确定。有关更多信息,请参见此处 - Matthew Beckman
2
我认为这并不会阻止渲染:https://w3c.github.io/preload/。例如,应用程序可以使用preload关键字来启动CSS资源的早期、高优先级和非渲染阻塞获取,然后在适当的时间由应用程序应用。 - crenshaw-dev
2
@MichaelCrenshaw 是正确的,<link rel="preload"> 不会阻止渲染,我认为作者误解了定义。来自 MDN 使用 rel="preload" 预加载内容 的描述是:“...在浏览器主渲染机制启动之前,您希望尽早开始加载的资源。” 这个想法是尽可能快地获取资源,使得当需要时,例如 <img> 元素正在渲染时,该资源更容易获得。 - MDMower
1
我想使用图片作为CSS的background-image,这对我起作用了,而被接受的答案则没有。 - CWSites
虽然预加载对我有用,但当CSS需要时,使用预取会再次下载图像。 - raquelhortab

36

我可以确认我的原始代码似乎有效。我无意中使用了一个错误的路径粘贴到了图像上。

这是一个测试:http://paragraphe.org/slidetoggletest/test.html

<script>
    var pic = new Image();
    var pic2 = new Image();
    var pic3 = new Image();
    pic.src="images/inputs/input1.png";
    pic2.src="images/inputs/input2.png";
    pic3.src="images/inputs/input3.png";
</script>

经过使用CSS-ONLY和这个解决方案进行了几次测试后,在我的情况下,这是最好的解决方案。 - zequinha-bsb
谢谢,这是最简单的 :) - BetaCoder
4
答案中的URL已过期。 - Brandon Buck
1
嗨,我重新上传了那个网址(http://paragraphe.org/slidetoggletest/test.html)上的内容,很抱歉之前错过了。谢谢。 - Peanuts

15

http://css-tricks.com/snippets/css/css-only-image-preloading/

技巧#1

在元素的普通状态下加载图像,只需使用背景位置将其移开。然后在悬停时移动背景位置以显示它。

#grass { background: url(images/grass.png) no-repeat -9999px -9999px; }
#grass:hover { background-position: bottom left; }

技巧 #2

如果需要更改某个元素的背景图像,但该元素已经应用了背景图像,则上述方法无法使用。通常情况下,您可以使用精灵图 (一种组合的背景图像) 并仅移动背景位置。但是,如果不可能使用精灵图,请尝试此方法。将背景图像应用于另一个已在使用中但没有背景图像的页面元素。

#random-unsuspecting-element { background: url(images/grass.png) no-repeat -9999px -9999px; }
#grass:hover { background: url(images/grass.png) no-repeat; }

4
请始终提供外部链接的内容。否则,如果链接失效,您的回答就毫无意义了! - sra
@Fatih,非常感谢你分享这个好链接。这里有一篇更新的文章,介绍了三种不同的预加载图片的方法:http://perishablepress.com/3-ways-preload-images-css-javascript-ajax/ - xmojmr
@xmojmr,你的链接中没有纯CSS的解决方案。 - Max

10

尝试使用以下代码:

var c=new Image("Path to the background image");
c.onload=function(){
   //render the form
}

使用这段代码可以预加载背景图像,在载入完成后渲染表单。


mck89,谢谢,这看起来很不错。但是,我应该将此代码放在“head”中还是内联?我的意思是,我必须用HTML填充它吗? - Peanuts
你必须在头部使用它。我认为你可以在$(document).ready内部使用它。 - mck89

7

对于使用 CSS 设置的预加载背景图像,我想到的最有效的答案是修改了一些我找到但不起作用的代码:

$(':hidden').each(function() {
  var backgroundImage = $(this).css("background-image");
  if (backgroundImage != 'none') {
    tempImage = new Image();
    tempImage.src = backgroundImage;
  }
});

这样做的巨大好处是,当您将来引入新的背景图像时,无需更新它,它会找到新图像并预加载它们!

我是否正确理解,您为每个图像添加了一个隐藏元素?它不能扫描您的CSS文件以查找图像,对吗? :) - bvdb

5
如果您在站点的其他位置重复使用这些背景图像以用于表单输入,则可能需要使用图像精灵。这样,您就可以集中管理图像(而不是拥有pic1、pic2、pic3等)。对于客户端来说,精灵通常更快,因为它们仅从服务器请求一个(稍大一些的)文件,而不是多个文件。有关更多好处,请参见SO文章: CSS image sprites 不过,如果您只是为一个表单使用这些背景图像,并且只想在用户请求联系表单时加载它们,那么这可能根本没有帮助...不过这也许是有道理的。

http://www.alistapart.com/articles/sprites


是的,CSS Sprites 是最好的选择。只需要确保该 Sprite 中的一张图片出现在隐藏表单被启动之前(例如页面加载时)。 - Steve

4
我也想添加一个小提示。
如果您在开发控制台设置的网络选项卡中禁用缓存,则“仅使用CSS预加载图像”的答案将完全无效。 否则它将导致图像被重复加载。
参考图像: enter image description here

1
唯一的方法是对图像进行Base64编码,然后将其放置在HTML代码中,这样就不需要联系服务器来下载图像。 这里会对URL中的图像进行编码,以便您可以复制图像文件代码并将其插入到您的页面中,如下所示...
body {
  background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADIA...);
}

2
如果您想在CSS中再次使用相同的图像...它不会被缓存,您将不得不重新加载它。 - vsync

1
当无法修改CSS代码并使用CSS规则预加载伪元素:before:after的图像时,可以使用JavaScript代码遍历已加载样式表的CSS规则的另一种方法。为了使其正常工作,需要在HTML中包含脚本应该在样式表之后,例如,在关闭 body 标签之前或仅在样式表之后。
getUrls() {
    const urlRegExp = /url\(('|")?([^'"()]+)('|")\)?/;

    let urls = [];
    for (let i = 0; i < document.styleSheets.length; i++) {
        let cssRules = document.styleSheets[i].cssRules;
        for (let j = 0; j < cssRules.length; j++) {
            let cssRule = cssRules[j];
            if (!cssRule.selectorText) {
                continue;
            }

            for (let k = 0; k < cssRule.style.length; k++) {
                let property = cssRule.style[k],
                    urlMatch = cssRule.style[property].match(urlRegExp);
                if (urlMatch !== null) {
                    urls.push(urlMatch[2]);
                }
            }
        }
    }
    return urls;
}

preloadImages() {
    return new Promise(resolve => {
        let urls = getUrls(),
            loadedCount = 0;

        const onImageLoad = () => {
            loadedCount++;
            if (urls.length === loadedCount) {
                resolve();
            }
        };

        for (var i = 0; i < urls.length; i++) {
            let image = new Image();
            image.src = urls[i];
            image.onload = onImageLoad;
        }
    });
}

document.addEventListener('DOMContentLoaded', () => {
    preloadImages().then(() => {
        // CSS images are loaded here
    });
});

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接