Saber 酱的抱枕

Fly me to the moon

03/24
2017
学习

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
2017
学习

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

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

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

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

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

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

我们可以将生成后的地图里的不存在的图片替换为地图生成器里的正常图片来解决问题。将下面的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
2017
学习 软件

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
2017
学习

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的使用

03/3
2017
学习

在JavaScript中使用document.all来判断是否是IE浏览器

今天看到一个代码,通过document.all函数判断浏览器是否是IE。代码大致是这样的:

 if (document.all){
 	// 是IE
 }else{
 	// 不是IE
 }

这里面有个有意思的地方。

document.all函数会返回页面上所有html元素的集合,现在主流浏览器均支持,比如在chrome中执行一下:

在JavaScript中使用document.all来判断是否是IE浏览器

在返回的集合中包含了页面上所有的html元素。

那么问题来了,既然chrome也支持这个函数,结果也不为空,这样判断时不应该是true吗?

试一下将其转换为布尔值,结果竟然是false!惊了!

在JavaScript中使用document.all来判断是否是IE浏览器

其实,在IE之外的浏览器里,运行 typeof document.all ,得到的都是结果都是undefined。而在IE里就是object。

这个现象是有历史原因的。document.all最早是由IE4实现的,当时很多程序猿就通过document.all来判断是否是IE浏览器。后来其他浏览器也实现了document.all,但是又不想被旧程序判断为IE浏览器,所以就把document.all的类型设置为undefined了。

这样,在IE之外的浏览器里判断document.all就会得到false了。

ps:IE11里判断document.all也是false了,所以此方法已经不可靠。

在JavaScript中使用document.all来判断是否是IE浏览器

03/2
2017
学习

JavaScript小数取整的3种方法

JavaScript中要对小数取整的话(单纯舍弃小数部分,不涉及四舍五入),有原生函数parseInt()。不过除此之外还有两种“歪门邪道”的写法,这三种写法合起来比较一下:

parseInt(10/3)
// 返回 3

~~(10/3)
// 返回 3

10/3|0
// 返回 3

其实第二种的波浪号和第三种的竖线都是“位操作符”。

注意:
第二种写法是两个波浪线哦。一个波浪线是达不到取整效果的。
第三种写法的竖线和0是一个整体哦,不然也是达不到取整效果的。

JavaScript的3种小数取整方法

参考来源

JavaScript小数取整的3种方法

02/17
2017
学习

移动设备触摸手势库——Hammer.JS

一直以来,JavaScript对于鼠标事件和触摸事件提供的支持都比较初级,此问题在pc端不是很明显,但是移动端的触摸手势就比较多样化了,为此我们有必要使用js库来帮助我们处理触摸事件。

Hammer.JS是一款轻量级的移动设备触摸手势库,不依赖其他js库,兼容性好(支持WP),支持常见的点击、拖动、缩放、旋转等触摸事件,也可支持多点触控。

Hammer.JS支持的六大事件:

移动设备触摸手势库——hammer.js

Hammer.JS支持在pc端浏览器上用鼠标模拟触摸事件,这样我们开发时可以在pc上进行测试,极大提高了开发效率(看它官网的描述,似乎是可以在pc上模拟出全部的6种动作。不过缩放和旋转我试不出来,按下shift模拟双指操作也没反应①)。

下面是一个左右滑动的示例:

var myElement =document.querySelector("#a");
var mc = new Hammer(myElement);
mc.on("swipeleft swiperight", function(ev) {
	if (ev.deltaTime>100) {	// 判断一下延迟时间,小于100ms的不处理
		if (ev.deltaX>0) {	//向右滑动,展示左侧内容
			// code
		}else if (ev.deltaX<0) {	//向左滑动,展示右侧内容
			// code
		}
	}
    console.log(ev);
});

hammer.js用起来还真是费了不少劲。

首先是它有好几个可以下载的地方,但不同的地方不但有的版本号不同,有些甚至完全不生效。我现在用的是https://hammerjs.github.io/dist/hammer.min.js。也可能是其他版本改了api?

第二是注意禁用浏览器自己的选择事件和拖动事件。

举个例子,网页中的图片是可以被拖动的。当你按下鼠标并移动来模拟滑动事件时,会发现图片变成了拖拽状态,这时候就触发不了hammer.js上绑定的事件了,所以要用css禁止拖动该元素。

拖动文字可能导致文字被选中,所以也要禁止选中该元素。

一开始我没发现这个事情,当我把可以正常使用的demo的代码移植到我的项目里之后,发现不生效,这让我郁闷了好久,最后琢磨出来是这个原因。

hammer.js会给直接绑定的元素自动增加禁止拖动和选择的属性,但如果该元素有子元素,则需要我们视情况对该处理的子元素进行处理。

hammer.js的使用说明也较少,所以有时候我们要输出ev来自己查看当前事件的一些属性。


参考资料1
移动端手势库hammerJS 2.0.4官方文档翻译


① 有资料说,pinch 和rotate 默认情况下是禁用的,因为他们会使元件闭锁(这里貌似是机翻),如有需要可以通过下面的方法调用:

hammertime.get('pinch').set({ enable: true });
hammertime.get('rotate').set({ enable: true });

不过我还没用到这个,没去试验。

移动设备触摸手势库——Hammer.JS

02/9
2017
学习

封装document.querySelector和document.querySelectorAll方法

JavaScript原生的document.querySelector和document.querySelectorAll方法很好用,不过依旧摆脱不了原生方法名太长的麻烦。所以我们可以封装下,如:

function $(selector) {
  return document.querySelector(selector)
}

function $$(selector) {
  return Array.prototype.slice.call(document.querySelectorAll(selector))
}

这里用了$、$$作为方法名,分别对document.querySelector方法和document.querySelectorAll方法进行封装。

$方法返回单个元素(第一个符合的元素),$$以数组形式返回所有符合的元素(即使符合的元素只有一个)。

这样我们就可以方便的使用封装的方法来选择DOM元素了:

$("#div .class img")
$$("#div .class img")

不过如果$、$$的定义冲突了可能会出问题,所以如果页面上引用jQuery了的话,可以换个方法名。

ps:至于为什么方法名要用$和$$呢,并不是模仿jQuery,而是模仿chrome(至于chrome是不是模仿jQuery的方法名那我就不清楚了)。

chrome浏览器控制台里内置了封装好的$、$$方法,可以直接使用。(当然,如果$、$$的定义被页面上的代码覆盖了就不行了)

如图:

封装document.querySelector和document.querySelectorAll方法

封装document.querySelector和document.querySelectorAll方法

封装document.querySelector和document.querySelectorAll方法

01/22
2017
学习

动态设置rem使页面自适应缩放

更新:两年过去了,现在推荐使用 vw + rem 自适应布局


我们页面布局中,可以通过动态设置rem并使用rem单位来设置宽高,来使得页面元素的尺寸可以随设备宽度而变化,这样就能保持布局的一致性。

使用JavaScript动态设置rem的代码如下:

(function (doc, win) {
	var docEl = doc.documentElement,
	  resizeEvt = 'orientationchange' in window ? 'orientationchange' : 'resize',
	  recalc = function () {
	      var clientWidth = docEl.clientWidth;
	      if (!clientWidth) return;
	      docEl.style.fontSize = 100 * (clientWidth / 640) + 'px';
	  };
    if (!doc.addEventListener) return;
    win.addEventListener(resizeEvt, recalc, false);
    doc.addEventListener('DOMContentLoaded', recalc, false);
})(document, window);

公式是这样的:

100 * ( 浏览器页面宽度 / 设计稿宽度 )

第一部分:100是什么

这个100其实是我们使用的rem基础值,等于设置html标签的font-size=100px,这样,1rem也就是100px。

当1rem为100px时,页面上所有元素设置宽高大小时都除以100,就得出了该元素的rem值。比如设计稿上300px宽的元素,用rem设置就是width=3rem。

1rem并不是非要设置为100px,我们也可以设置成别的,比如设置1rem=50px,那么300px的元素是6rem。我们一般取1rem=100px是为了方便计算。如果实际项目中计算页面元素的rem值时不是按1rem=100px来算的,则需要修改公式里的100为实际使用的1rem值。

第二部分:浏览器页面宽度 / 设计稿宽度 是什么

这个可以理解为页面在设备上的缩放比例。比如iphone 6 plus的设备宽度是414px,我们的设计稿是640px。

414/640=0.646875,得出我们的页面在iphone 6 plus上应该显示为设计稿大小的0.646875倍。

最后我们把rem的基础值与页面缩放比例相乘,得出实际使用的rem基础值。rem的基础值变了,页面上所有元素的宽高、大小也会随之改变,这样就达到了页面自适应的效果。
Read More →

动态设置rem使页面自适应缩放