整合营销服务商

电脑端+手机端+微信端=数据同步管理

免费咨询热线:

js如何控制一次只加载一张图片,加载完成后再加载下一张

天看到一个面试题,是关于img图片加载方面的,有必要记录一下。其实关于这个问题,只要知道图片什么时候加载完成就能解决了。

通过onload事件判断Img标签加载完成

实现逻辑:新建一个Image对象实例,为实例对象设置src属性等,在onload事件中添加此实例对象到父元素中,然后将图片地址数组中的第一个元素剔除,继续调用此方法直到存储图片地址的数组为空。

代码

const imgArrs = [...]; // 图片地址
const content = document.getElementById('content');
const loadImg = () => {
if (!imgArrs.length) return;
const img = new Image(); // 新建一个Image对象
img.src = imgArrs[0];
img.setAttribute('class', 'img-item');
img.onload = () => { // 监听onload事件
// setTimeout(() => { // 使用setTimeout可以更清晰的看清实现效果
content.appendChild(img);
imgArrs.shift();
loadImg();
// }, 1000);
}
img.onerror = () => {
// do something here
}
}
loadImg();

</script>

实现效果

加上setTimeout后,看到的效果更加明显,我这里加了500毫秒的延迟(录屏软件只支持录制8秒的时间...)

其实我在网上还看到了一种答案,通过onreadystatechange事件实现监听,于是在我本地调试了一下,发现并不能实现,img实例对象上并没有这个属性方法。查了查MDN,发现目前仅有XmlHttpRequest对象和Document对象中存在onreadystatechange属性,而对于其它元素onreadystatechange此属性并不存在。

因此对于其它元素需要慎用onreadystatechange事件

不过我电脑上目前只有Chorme和Safari两种浏览器,对于onreadystatechange测试的覆盖面不全,所以我上面的结论可能还需要进一步验证才行,感兴趣的掘友可以调试一下~。

扩展知识

img标签是什么时候发送图片资源请求的?

  1. HTML文档渲染解析,如果解析到img标签的src时,浏览器就会立刻开启一个线程去请求图片资源。
  2. 动态创建img标签,设置src属性时,即使这个img标签没有添加到dom元素中,也会立即发送一个请求。
// 例1:
const img = new Image();
img.src = 'http://xxxx.com/x/y/z/ccc.png';

上面的代码如果运行起来后,就会发送请求。 如图:

再看一个例子:创建了一个div元素,然后将存放img标签元素的变量添加到div元素内,而div元素此时并不在dom文档中,页面不会展示该div元素,那么浏览器会发送请求吗?

// 例2:
const img = `<img src='http://xxxx.com/x/y/z/ccc.png'>`;
const dom = document.createElement('div');
dom.innerHtml = img;

答案:会请求。如图:

通过设置css属性能否做到禁止发送图片请求资源?

  1. 给img标签设置样式display:none或者visibility: hidden,隐藏img标签,无法做到禁止发送请求。
<img src="http://xxx.com/x/sdf.png" style="display: none;">
或者
<img src="http://xxx.com/x/sdf.png" style="visibility: hidden;">
  1. 将图片设置为元素的背景图片,但此元素不存在,可以做到禁止发送请求。
<!DOCTYPE html>
<html lang='en'>
<head>
<meta charset='UTF-8'>
<title></title>
<style>
.test {
height: 200px;
background-image: url('http://eb118-file.cdn.bcebos.com/upload/39148b2a545b48bf9b4ee95fd1b7f1eb_1515564089.png?');
}
</style>
</head>
<body>
<div></div>
</body>
</html>

dom文档中不存在test元素时,即使设置了背景图片,也不会发送请求,只有test元素存在时才会发送请求。

另外这个例子其实有点不太贴切,img标签和background-image二者有着本质的区别。一个属于HTML标签,另一个属于css样式,加载机制和解析顺序也不同。

一个完整的页面是由js、html、css组成的,按照解析机制,html元素会优先解析,尽管css样式是放在head标签内的,但也不意味着它会优先加载,它只有等到html文档加载完成后才会执行。而img标签属于网页内容,所以img标签会随着网页解析渲染优先于css样式表加载出来。


作者:娜个小部呀
链接:https://juejin.cn/post/7340167256267391012

事跟我说他用jQuery取不到页面上隐藏元素input的值,他的html页面大概内容如下。

<!DOCTYPE html>
<html lang="zh">
 
<head>
	<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
	<script type="text/javascript" src="jslib/jquery-1.11.2.min.js"></script>
	<title>浅谈Html页面内容执行顺序</title>
	<script type="text/javascript">
		var userId = $('#hiddenUserId').val();
		var contextPath = $('#hiddenContextPath').val();
		var userName = $('#hiddenUserName').val();
	</script>
</head>
 
<body>
	<input type="hidden" id="hiddenUserId" value="101" />
	<input type="hidden" id="hiddenContextPath" value="/web" />
	<input type="hidden" id="hiddenUserName" value="小明" />
</body>
 
</html>

页面中的JS脚本在head中,JS脚本要读取的input在body中。浏览器对html页面内容的加载是顺序加载,也就是在html页面中前面先加载,因此当加载到JS脚本时,input还没有加载到浏览器中。JS是一种解释性的脚本,也是从上而下顺序执行,由于这段JS代码是立即执行的,所以当JS在执行的时候,读取不到input的值。

最直接的修改方法是把JS放到网页的最下面执行。

<!DOCTYPE html>
<html lang="zh">
 
<head>
	<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
	<script type="text/javascript" src="jslib/jquery-1.11.2.min.js"></script>
	<title>浅谈Html页面内容执行顺序</title>	
</head>
 
<body>
	<input type="hidden" id="hiddenUserId" value="101" />
	<input type="hidden" id="hiddenContextPath" value="/web" />
	<input type="hidden" id="hiddenUserName" value="小明" />
	
	<script type="text/javascript">
		var userId = $('#hiddenUserId').val();
		var contextPath = $('#hiddenContextPath').val();
		var userName = $('#hiddenUserName').val();
	</script>
</body>
 
</html>

把JS放到网页的最下面,这样在JS执行的时候,网页内容都已经加载完毕。把JS放在网页的最下面方法并不是最好的解决方法,大部分情况JS并不是总能放在网页的最下面。这时可以用window的onload事件,onload事件在整个页面都加载完成后才触发,可以把JS脚本放在onload里面执行。不同浏览器onload事件添加方式也不一样。

IE下事件:

window.attachEvent('onload', function(){
			var userId = $('#hiddenUserId').val();
			var contextPath = $('#hiddenContextPath').val();
			var userName = $('#hiddenUserName').val();
		});

Chrome/Firefox等DOM标准事件:

window.addEventListener('load', function(){
			var userId = $('#hiddenUserId').val();
			var contextPath = $('#hiddenContextPath').val();
			var userName = $('#hiddenUserName').val();
		});

由于不同浏览器的事件添加方式不一样,jQuery为我们提供了通用的初始化方法,该方法在页面加载完成时触发。

$(function(){
			var userId = $('#hiddenUserId').val();
			var contextPath = $('#hiddenContextPath').val();
			var userName = $('#hiddenUserName').val();
		});

上面方法本质就是添加onload监听事件。

最终修改后的页面

加载图片是提高用户体验的一个很好方法。图片预先加载到浏览器中,访问者便可顺利地在你的网站上冲浪,并享受到极快的加载速度。

这对图片画廊及图片占据很大比例的网站来说十分有利,它保证了图片快速、无缝地发布,也可帮助用户在浏览你网站内容时获得更好的用户体验。本文将分享三个不同的预加载技术,来增强网站的性能与可用性。

方法一:用css和JavaScript实现预加载

实现预加载图片有很多方法,包括使用css、JavaScript及两者的各种组合。这些技术可根据不同设计场景设计出相应的解决方案,十分高效。

单纯使用CSS,可容易、高效地预加载图片,代码如下:

#preload-01 { background: url(http://domain.tld/image-01.png) no-repeat -9999px -9999px; }  
#preload-02 { background: url(http://domain.tld/image-02.png) no-repeat -9999px -9999px; }  
#preload-03 { background: url(http://domain.tld/image-03.png) no-repeat -9999px -9999px; }

将这三个ID选择器应用到(X)html元素中,我们便可通过CSS的background属性将图片预加载到屏幕外的背景上。

只要这些图片的路径保持不变,当它们在Web页面的其他地方被调用时,浏览器就会在渲染过程中使用预加载(缓存)的图片。简单、高效,不需要任何JavaScript。

该方法虽然高效,但仍有改进余地。使用该法加载的图片会同页面的其他内容一起加载,增加了页面的整体加载时间。

为了解决这个问题,我们增加了一些JavaScript代码,来推迟预加载的时间,直到页面加载完毕。代码如下:

function preloader() {  
    if (document.getElementById) {  
        document.getElementById("preload-01").style.background = "url(http://domain.tld/image-01.png) no-repeat -9999px -9999px";  
        document.getElementById("preload-02").style.background = "url(http://domain.tld/image-02.png) no-repeat -9999px -9999px";  
        document.getElementById("preload-03").style.background = "url(http://domain.tld/image-03.png) no-repeat -9999px -9999px";  
    }  
}  
function addLoadEvent(func) {  
    var oldonload = window.onload;  
    if (typeof window.onload != 'function') {  
        window.onload = func;  
    } else {  
        window.onload = function() {  
            if (oldonload) {  
                oldonload();  
            }  
            func();  
        }  
    }  
}  
addLoadEvent(preloader);

在该脚本的第一部分,我们获取使用类选择器的元素,并为其设置了background属性,以预加载不同的图片。

该脚本的第二部分,我们使用addLoadEvent()函数来延迟preloader()函数的加载时间,直到页面加载完毕。

如果JavaScript无法在用户的浏览器中正常运行,会发生什么?很简单,图片不会被预加载,当页面调用图片时,正常显示即可。

方法二:仅使用JavaScript实现预加载

上述方法有时确实很高效,但我们逐渐发现它在实际实现过程中会耗费太多时间。相反,我更喜欢使用纯JavaScript来实现图片的预加载。

下面将提供两种这样的预加载方法,它们可以很漂亮地工作于所有现代浏览器之上。

JavaScript代码段1

只需简单编辑、加载所需要图片的路径与名称即可,很容易实现:

<div class="hidden">  
    <script type="text/javascript">  
           var images = new Array()  
            function preload() {  
                for (i = 0; i < preload.arguments.length; i++) {  
                    images[i] = new Image()  
                    images[i].src = preload.arguments[i]  
                }  
            }  
            preload(  
                "http://domain.tld/gallery/image-001.jpg",  
                "http://domain.tld/gallery/image-002.jpg",  
                "http://domain.tld/gallery/image-003.jpg"  
            )  
</script> 
</div>

该方法尤其适用预加载大量的图片。我的画廊网站使用该技术,预加载图片数量达50多张。将该脚本应用到登录页面,只要用户输入登录帐号,大部分画廊图片将被预加载。

JavaScript代码段2

该方法与上面的方法类似,也可以预加载任意数量的图片。将下面的脚本添加入任何Web页中,根据程序指令进行编辑即可。

<div class="hidden">  
    <script type="text/javascript">  
            if (document.images) {  
                img1 = new Image();  
                img2 = new Image();  
                img3 = new Image();  
                img1.src = "http://domain.tld/path/to/image-001.gif";  
                img2.src = "http://domain.tld/path/to/image-002.gif";  
                img3.src = "http://domain.tld/path/to/image-003.gif";  
            }    
</script>  
</div>

正如所看见,每加载一个图片都需要创建一个变量,如“img1 = new Image();”,及图片源地址声明,如“img3.src = “../path/to/image-003.gif”;”。参考该模式,你可根据需要加载任意多的图片。

我们又对该方法进行了改进。将该脚本封装入一个函数中,并使用 addLoadEvent(),延迟预加载时间,直到页面加载完毕。

function preloader() {  
    if (document.images) {  
        var img1 = new Image();  
        var img2 = new Image();  
        var img3 = new Image();  
        img1.src = "http://domain.tld/path/to/image-001.gif";  
        img2.src = "http://domain.tld/path/to/image-002.gif";  
        img3.src = "http://domain.tld/path/to/image-003.gif";  
    }  
}  
function addLoadEvent(func) {  
    var oldonload = window.onload;  
    if (typeof window.onload != 'function') {  
        window.onload = func;  
    } else {  
        window.onload = function() {  
            if (oldonload) {  
                oldonload();  
            }  
            func();  
        }  
    }  
}  
addLoadEvent(preloader);

方法三:使用Ajax实现预加载

上面所给出的方法似乎不够酷,那现在来看一个使用Ajax实现图片预加载的方法。该方法利用DOM,不仅仅预加载图片,还会预加载CSS、JavaScript等相关的东西。使用Ajax,比直接使用JavaScript,优越之处在于JavaScript和CSS的加载不会影响到当前页面。该方法简洁、高效。

window.onload = function() {  
    setTimeout(function() {  
        // XHR to request a js and a CSS  
        var xhr = new XMLHttpRequest();  
        xhr.open('GET', 'http://domain.tld/preload.js');  
        xhr.send('');  
        xhr = new XMLHttpRequest();  
        xhr.open('GET', 'http://domain.tld/preload.css');  
        xhr.send('');  
        // preload image  
        new Image().src = "http://domain.tld/preload.png";  
    }, 1000);  
};

上面代码预加载了“preload.js”、“preload.css”和“preload.png”。1000毫秒的超时是为了防止脚本挂起,而导致正常页面出现功能问题。

下面,我们看看如何用JavaScript来实现该加载过程:

window.onload = function() {  
    setTimeout(function() {  
        // reference to <head>  
        var head = document.getElementsByTagName('head')[0];  
        // a new CSS  
        var css = document.createElement('link');  
        css.type = "text/css";  
        css.rel  = "stylesheet";  
        css.href = "http://domain.tld/preload.css";  
        // a new JS  
        var js  = document.createElement("script");  
        js.type = "text/javascript";  
        js.src  = "http://domain.tld/preload.js";  
        // preload JS and CSS  
        head.appendChild(css);  
        head.appendChild(js);  
        // preload image  
        new Image().src = "http://domain.tld/preload.png";  
    }, 1000); 
};

这里,我们通过DOM创建三个元素来实现三个文件的预加载。正如上面提到的那样,使用Ajax,加载文件不会应用到加载页面上。从这点上看,Ajax方法优越于JavaScript。


- End -