saber酱的抱枕

努力变强

04/11
09:03
学习

JavaScript回收DOM节点的内存

如果我们要删除一个DOM节点,可能大家都会想到remove方法。如下:

var a=document.createElement("a");
// 设置内容等代码 ...
a.parentNode.removeChild(a);

Node.removeChild() 方法从DOM中删除一个子节点。返回删除的节点。

从返回值是个节点我们可以知道,removeChild之后,这个节点虽然在DOM里去掉了,但是它仍然存在在内存里。

虽说过一段时间不用,它就会被浏览器回收掉,但如果要重复的创建这个节点的话,那么每次removeChild时使用delete操作符,可以略微优化内存占用(大概……)。

如下:

var a=document.createElement("a");
// 设置内容等代码 ...
delete a.parentNode.removeChild(a);

今天我在chrome上做了多次实验,有一些发现:

1.使用delete之后,chrome浏览器并不会立即回收这部分内存,而是等差不多二十秒之后才回收。

2.使用delete之后,内存回收的比只remove要多一些。

3.即使使用delete,内存也不能完全回收,内存占用还是会一直增加。只是情况略有缓解。

4.有人说delete回收内存只能回收由JavaScript创建的节点,如果是原本html文档里的节点,delete是回收不了的。我试了试,对于原本html文档里的节点,用delete和只用remove没看出明显区别,大概这个说法是对的。

JavaScript回收DOM节点的内存

04/8
17:33
学习

使用canvas将图片转换为base64编码

如果我们要在网页上处理图片,对于字符形式接收到的图片(比如用ajax获取的图片)可以将其转换为blob对象。但对于img标签(或通过new Image()对象获取的图片)来说,它们不是字符形式,而是html元素,不能转换成blob对象。

要使用这样的图片,我们可以先将图片绘制到canvas里,再使用canvas对象的toDataURL方法获取图片的base64编码(也就是data: URI)。

ps:blob对象和data: URI都可以用a标签的download属性来下载图片~

1.如下代码展示用image对象获取一张图片并转换为base64:

var dataURL="";
var Img = new Image();
Img.crossOrigin = "Anonymous";
Img.src="/f/head15.jpg";
Img.onload=function(){
	var canvas = document.createElement("canvas");
    canvas.width=Img.width;
    canvas.height=Img.height;
    canvas.getContext("2d").drawImage(Img,0,0,Img.width,Img.height); //将图片绘制到canvas中
    dataURL=canvas.toDataURL('image/png'); //转换图片为dataURL
};

注意image对象的crossOrigin 属性:

Img.crossOrigin = "Anonymous";

它在下面的情况中是有用的:
图片跨域了,但有个好消息是图片的服务器允许跨域。这时你加上这个crossOrigin属性就ok了,图片可以正常用。

但如果图片跨域了,并且图片的服务器【不允许】跨域,那你加上也没用,无解。

如果没解决跨域限制,canvas.toDataURL方法将报错,导致程序终止:

Uncaught DOMException: Failed to execute 'toDataURL' on 'HTMLCanvasElement': Tainted canvases may not be exported.

2.如下代码展示从页面上的img标签获取图片内容,并将其转换为base64:

// 记得要在图片加载完成后执行
var image=document.querySelector("img"),
	canvas,
	dataURL;
image.onload=function () {
	canvas = document.createElement("canvas");
	canvas.width = image.width;
	canvas.height = image.height;
	canvas.getContext("2d").drawImage(image, 0, 0);
	dataURL=canvas.toDataURL('image/png'); //转换图片为dataURL
};

3.如果我们是要把图片用于下载的,那么对于色彩复杂、宽高较大的图片,用png格式显然体积会很大,可以转换成jpg的:

dataURL=canvas.toDataURL('image/jpeg',0.5);

经试验完全可用。另外chrome的话还可指定为 image/webp 。

参考资料来自MDN

4.扩展:现在各大浏览器只支持转换成png和jpeg格式,如果需要gif格式或bmp格式怎么办呢?可以用canvas2image.js

canvas2image支持将canvas里的图片数据转换为png、jpeg、gif、bmp格式,并可修改生成的图片的宽高。

你可以仅转换但不下载,也可直接下载。like this:

Canvas2Image.saveAsImage(canvasObj, width, height, type)
Canvas2Image.saveAsPNG(canvasObj, width, height)
Canvas2Image.saveAsJPEG(canvasObj, width, height)
Canvas2Image.saveAsGIF(canvasObj, width, height)
Canvas2Image.saveAsBMP(canvasObj, width, height)

Canvas2Image.convertToImage(canvasObj, width, height, type)
Canvas2Image.convertToPNG(canvasObj, width, height)
Canvas2Image.convertToJPEG(canvasObj, width, height)
Canvas2Image.convertToGIF(canvasObj, width, height)
Canvas2Image.convertToBMP(canvasObj, width, height)

使用canvas将图片转换为base64编码

04/1
08:00
学习

JavaScript使代码延迟一段时间执行

JavaScript里说到延迟执行,一般想到的就是定时器。而且原生的方法里除了定时器,也没什么别的办法了。

但是定时器其实是一种插入机制,只针对它内部的代码做延迟,不能延迟整体代码的执行。

如果在某个时候,我们想让后面的代码全部延迟一定时间执行,只能把后面的代码全都放进定时器(或者改一下代码,只放调用部分)。多嵌套了一层,在代码的阅读和理解上产生了负面效果,很不舒服。

想要实现类似于其他程序的sleep函数的效果,我们可以用笨办法来做一个,实现整体代码的延迟。

示例:

function sleep(milliSeconds){
    var startTime = new Date().getTime();
    while (new Date().getTime() < startTime + milliSeconds);
}
console.log("1");
sleep(1000);
console.log("2");

不过使用sleep需要小心,因为它是整体延迟,会阻塞JavaScript进程,导致页面也卡住(类似于ajax的同步模式)。

比如上例代码,延迟一秒。如果在页面加载时执行,由于这一秒里JavaScript没执行完,所以页面加载时间就会增加一秒(+1s?),同时在这1秒里无法对页面进行操作。

那如果在页面加载完成后执行呢?这也会导致页面卡住1秒,因为sleep的代码导致CPU占用非常高,页面除了循环什么都不做了,导致用户在这1秒里无法对页面进行操作。

所以这个sleep虽然在效果上是真正的sleep(参考其他编程语言),但还是需要确认使用场景才能用,不能乱用。毕竟后台sleep用户只是多等了1秒,前台sleep却会导致页面卡住,对用户操作会产生直接的影响。

JavaScript使代码延迟一段时间执行

03/31
11:20
学习

在JavaScript中创建Blob对象

JavaScript中的Blob对象可以存储二进制数据,并且还可以为数据指定MINE类型,因此适合在网页上储存文件用。

创建Blob对象需要使用new Blob()构造函数,它的语法如下:

Blob(blobParts[, options])

它有两个参数,第一个参数blobParts是要转化为Blob对象的数据,它必须在一个数组里。即使这份数据本来是独立的,也要创建一个数组把它包含到数组里。
第二个参数是可选的,使用Object类型的键值对来指定MINE类型。

示例:

var data='<b>aaa</b>';   
var blob=new Blob([data],{"type":"text/html"});   
console.log(blob);  

上例中是储存了一个html类型的Blob对象,实际使用中我们也可以储存其他文件类型,常见的有图片、json等。

相关资料:MDN上的相关内容
Read More →

在JavaScript中创建Blob对象

03/29
08:00
学习 软件

仙尊的百度贴吧小尾巴

仙尊的百度贴吧小尾巴 UserScript

今天想找个百度贴吧小尾巴的脚本,但是有些功能太多,我用不着那么复杂的,所以自己写了个简单的。(简陋到没有前台设置界面)

安装此用户脚本

可以插入一条文字内容和(或)一张图片,分别在两个变量里设置(在脚本里修改):

仙尊的百度贴吧小尾巴 UserScript

如果两条都设置了内容,那么就会一起发出去。你也可以清空文字或图片对应的内容,之后就不会追加那条内容了。

要使用时,使用快捷键Shift+Enter提交,就会加上小尾巴。如果使用贴吧的正常方式——Ctrl+Enter或者点击提交按钮,就不会加上小尾巴。

仙尊的百度贴吧小尾巴

03/26
08:00
软件

仙尊manhuagui、omanhua漫画下载器

本脚本用来批量下载manhuagui.com(漫画柜,原爱看漫)和omanhua.com(哦漫画)的漫画,在漫画阅读页任意一页里,都会在漫画上面出现一个“开始下载”按钮,点击即可下载。

使用方法:

安装本脚本,之后在ikanman和omanhua的漫画阅读页面就可以看到下载按钮啦~点击即可下载。

如果你不知道该怎么安装,请看greasyfork首页的教程。

下载按钮:

ikanman javascript js php UserScript 下载器 工具 漫画 爱看漫

下载结果:

ikanman javascript js php UserScript 下载器 工具 漫画 爱看漫

下载的图片会以浏览器下载的方式,保存到下载文件夹里。

本工具从任意页面下载皆可,会从当前页面一直下载到最后一页(会自动跨章节下载)。

下载后顶部会出现进度提示,点击进度提示可以停止下载。


注意:

最近manhuagui的图片格式有很多变成了.jpg.webp格式的,windows原生不支持此格式。有两个解决办法:

1.安装“WebP Codec for Windows”这个程序,安装之后可以使用windows图片查看器查看webp格式的图片。

2.安装支持webp格式的图片查看器,比如honeyview。
Read More →

仙尊manhuagui、omanhua漫画下载器

03/24
08:00
学习

JavaScript处理加载失败的图片

最近我修改某个网站,删除了很多上传的图片,但是又懒得去文章里一篇篇去掉出错的图片(任务量太大),所以就想到了用JavaScript在网页上找出出错的图片,并将其隐藏。

一开始我自己尝试了写了一些代码试试,主要是使用img元素的onerror事件。虽然代码在demo中达到了预期效果,但是加在实际页面里却不生效。对此我有一些猜测,但尚不能确定准确原因,后来还是用谷歌搜了现成的代码。

document.addEventListener("error", function(e){
    var elem = e.target;
    if(elem.tagName.toLowerCase() === 'img'){
    	// 如果引发error事件的元素是img元素,就进行处理
        elem.style.display="none";		//隐藏该图片
    	// elem.src = "/img/hint.jpg";	//或者替换为其他图片
    }
}, true /*指定事件处理函数在捕获阶段执行*/);

将这份代码加在网页开头(所有img元素之前)即可。

参考:《以全局监听的方式处理img的error事件》

JavaScript处理加载失败的图片

03/17
13:52
学习

解决百度地图生成器的标注图片失效问题

今天我使用百度地图生成器制作在线地图之后,发现了标注图片失效的问题。

正常时:
解决百度地图生成器的标注图片失效问题

生成后:
解决百度地图生成器的标注图片失效问题

搜了下这个情况,好像是去年底出现的,官方论坛上有人反映但一直没有恢复。

后来我找到了原因。我们使用百度地图生成器添加标注的时候是正常的,因为当时显示的是确实存在的图片。后来生成的地图里,标注图标使用的图片是另一个图片,但是这个图片已经不存在了,所以就出现了这个问题。

我们可以将生成后的地图里的不存在的图片替换为地图生成器里的正常图片来解决问题。将下面的JavaScript代码加在百度地图代码的页面里即可:

var bzT=window.setInterval(function () {
    if (!!document.querySelector("#platform > div:nth-child(2) > div:nth-child(5) > span > div > img")) {
         document.querySelector("#platform > div:nth-child(2) > div:nth-child(5) > span > div > img").src="http://api.map.baidu.com/lbsapi/creatmap/images/us_mk_icon.png";
         clearInterval(bzT);
    }
},500)

解决百度地图生成器的标注图片失效问题

03/11
08:00
学习 软件

GM_xmlhttpRequest获取响应头

JavaScript是可以获取Response Header(响应头)的信息的:

如果是使用XMLHttpRequest, 这个对象本身有一个getResponseHeader(DOMString header)的方法来获取。
如果是使用jQuery.ajax, 在success属性对应的回调函数中,第三个参数会被jQuery设置一个jqXHR的对象,这个对象是对XMLHttpRequest对象的一个封装,也是有getResponseHeader方法的。

不过我这里要说的是GM_xmlhttpRequest,也就是俗称的“油猴脚本”里的功能。在firefox里的油猴扩展是Greasemonkey,在chrome中则是Tampermonkey。它们都内置了GM_xmlhttpRequest方法,用于处理ajax事件。

GM_xmlhttpRequest有个很好用的地方是它不会受跨域限制(当油猴脚本跨域时,会先提示你是否允许跨域,允许即可)。

要使用GM_xmlhttpRequest,需要先在脚本头部引入GM_xmlhttpRequest:

// @grant        GM_xmlhttpRequest

然后用GM_xmlhttpRequest的head方法获取响应头:

GM_xmlhttpRequest({
	url: "url",
	method: "HEAD",
	onload: function(response) {
		console.log(response.responseHeaders);
	}
});

我获取了p站一个图片的响应头,输出类似下面:

Date: Tue, 07 Mar 2017 05:31:54 GMT
X-Content-Type-Options: nosniff
Last-Modified: Tue, 08 Mar 2016 09:00:36 GMT
Server: nginx
Content-Type: image/jpeg
Cache-Control: max-age=31536000
Accept-Ranges: bytes
Content-Length: 528794
Expires: Fri, 02 Mar 2018 10:41:44 GMT

不过我现在还有个疑惑,就是油猴怎么从响应头中提取某个特性属性的值。它好像没有getResponseHeader方法。

参考文档
Read More →

GM_xmlhttpRequest获取响应头

03/7
08:00
学习

JavaScript中event.currentTarget的使用

我最近做了一个带下拉菜单的导航,学到了event.currentTarget的使用。

demo如下:

代码:

<nav>
	<ul>
		<li class="lelve1">
			<a href="">下拉菜单</a>
			<div class="subNav">
				<a href="">企业简介</a>
				<a href="">发展历程</a>
				<a href="">主营业务</a>
			</div>
		</li>
	</ul>
</nav>
<script src="http://apps.bdimg.com/libs/jquery/2.1.4/jquery.min.js"></script>
<script>
	var isHover=false;
	$(".lelve1").mouseenter(
		function (event) {
			isHover=true;
			$(this).find(".subNav").fadeIn();
			//$(event.currentTarget).find(".subNav").fadeIn();
		}
	);
	$(".lelve1").mouseleave(
		function (event) {
			isHover=false;
			var a=$(this);
			setTimeout(function () {
				if (!isHover) {
					a.find('.subNav').fadeOut();
					//$(event.currentTarget).find(".subNav").fadeOut();
				}
			},50)
		}
	);
</script>

因为鼠标进入、鼠标离开事件都是绑定在一级导航上的,所以操作子导航的显示、隐藏时,都是通过在一级导航的元素里寻找子元素达到的:

$(this).find(".subNav").fadeIn();
a.find('.subNav').fadeOut();

这时都是使用this来指代一级导航,但除了使用this,还有另一个办法,就是使用event.currentTarget。

那event.currentTarget是什么呢?event.currentTarget指向事件所绑定的元素。

我们的鼠标进入、鼠标离开都是绑定在一级导航上,所以event.currentTarget所指向的元素始终是一级导航,使用起来很方便,而且可靠。

this在多层事件函数嵌套时经常会发生改变。但如果使用event.currentTarget的话,在这个作用域链里,event.currentTarget都不会变。所以我觉得event.currentTarget更方便可靠。


ps:event的属性里有一个大概比event.currentTarget更常用的,是event.target,指向触发该事件的元素。

event.currentTarget指向事件所绑定的元素,而event.target始终指向事件发生时的元素。

以前我有个错误的认知,以为哪个元素绑定的事件,那么触发事件的元素也是这个元素。其实不是的,像本例,当鼠标从子导航上离开时,触发一级导航mouseleave事件的元素(event.target)是a标签,而不是一级导航li标签。

JavaScript中event.currentTarget的使用