前端大本营

HTML5,CSS3,Responsive,资料站

利用CSS3动画制作你的动画加载页面

制作工具
  1. WOW.js
  2. Animate.css
制作方法

将css及js加载到页面

<link rel="stylesheet" type="text/css" media="all" href="/js/animate.css" />
<script type="text/javascript" src="/js/wow.min.js"></script>

在HTML中需要加载动画的部分,添加class名

<div class="wow slideInLeft"></div>  
<div class="wow slideInRight"></div>

可以加入 data-wow-duration(动画持续时间)和 data-wow-delay(动画延迟时间)属性,如:

<div class="wow slideInLeft" data-wow-duration="2s" data-wow-delay="5s"></div>  
<div class="wow slideInRight" data-wow-offset="10"  data-wow-iteration="10"></div> 

执行js

var wow = new WOW({  
    boxClass: 'wow',  /*指定动画元素的class名*/
    animateClass: 'animated',  
    offset: 0,  /*距离可视区域多少开始执行动画*/
    mobile: true,  /*是否在移动设备上执行动画*/
    live: true  /*异步加载的内容是否有效*/
});  
wow.init();  
Tags: ,
Posted in 精彩内容 | 利用 CSS3 技术制作屏幕滚动 动画加载内容的效果已关闭评论

前言

Notification API 是 HTML5 新增的桌面通知 API,用于向用户显示通知信息。该通知是脱离浏览器的,即使用户没有停留在当前标签页,甚至最小化了浏览器,该通知信息也一样会置顶显示出来。

用户权限

想要向用户显示通知消息,需要获取用户权限,而相同的域名只需要获取一次权限。只有用户允许的权限下,Notification 才能起到作用,避免某些网站的广告滥用 Notification 或其它给用户造成影响。那么如何知道用户到底是允不允许的?
Notification.permission 该属性用于表明当前通知显示的授权状态,可能的值包括:

  • default :不知道用户的选择,默认。
  • granted :用户允许。
  • denied :用户拒绝。
if(Notification.permission === 'granted'){
    console.log('用户允许通知');
}else if(Notification.permission === 'denied'){
    console.log('用户拒绝通知');
}else{
    console.log('用户还没选择,去向用户申请权限吧');
}

请求权限

当用户还没选择的时候,我们需要向用户去请求权限。Notification 对象提供了 requestPermission() 方法请求用户当前来源的权限以显示通知。

以前基于回调的语法已经弃用(当然在现在的浏览器中还是能用的),最新的规范已将此方法更新为基于 promise 的语法:

Notification.requestPermission().then(function(permission) {
    if(permission === 'granted'){
        console.log('用户允许通知');
    }else if(permission === 'denied'){
        console.log('用户拒绝通知');
    }
});

推送通知

获取用户授权之后,就可以推送通知了。

var notification = new Notification(title, options)
参数如下:
  • title:通知的标题
  • options:通知的设置选项(可选)。
    • body:通知的内容。
    • tag:代表通知的一个识别标签,相同tag时只会打开同一个通知窗口。
    • icon:要在通知中显示的图标的URL。
    • image:要在通知中显示的图像的URL。
    • data:想要和通知关联的任务类型的数据。
    • requireInteraction:通知保持有效不自动关闭,默认为false。

还有一些其他的参数,因为用不了或者没什么用这里就没必要说了。

var n = new Notification('状态更新提醒',{
    body: '你的朋友圈有3条新状态,快去查看吧',
    tag: 'linxin',
    icon: 'http://blog.gdfengshuo.com/images/avatar.jpg',
    requireInteraction: true
})
通知消息的效果图如下:

关闭通知

从上面的参数可以看出,并没有一个参数用来配置显示时长的。我想要它 3s 后自动关闭的话,这时可以调用 close() 方法来关闭通知。
var n = new Notification('状态更新提醒',{
    body: '你的朋友圈有3条新状态,快去查看吧'
})

setTimeout(function() {
    n.close();
}, 3000);

事件

Notification 接口的 onclick属性指定一个事件侦听器来接收 click 事件。当点击通知窗口时会触发相应事件,比如打开一个网址,引导用户回到自己的网站去。
var n = new Notification('状态更新提醒',{
    body: '你的朋友圈有3条新状态,快去查看吧',
    data: {
        url: 'http://blog.gdfengshuo.com'
    }
})
n.onclick = function(){
    window.open(n.data.url, '_blank');      // 打开网址
    n.close();                              // 并且关闭通知
}

应用场景

前面说那么多,其实就是为了用。那么到底哪些地方可以用到呢?

现在网站的消息提醒,大多数都是在消息中心显示个消息数量,然后发邮件告诉用户,这流程完全没有错。不过像我这种用户,觉得别人点个赞,收藏一下都要发个邮件提醒我,老是要去删邮件(强迫症),我是觉得挺烦的甚至关闭了邮件提醒。

当然这里并不是说要用 Notification,毕竟它和邮件的功能完全不一样。

我觉得比较适合的是新闻网站。用户浏览新闻时,可以推送给用户实时新闻。以腾讯体育为例,它就使用了 Notification API。在页面中引入了一个 notification2017_v0118.js,有兴趣可以看看别人是怎么成熟的使用的。

一进入页面,就获取授权,同时自己页面有个浮动框提示你允许授权。如果允许之后,就开始给你推送通知了。不过它在关闭标签卡的时候,通知也会被关闭,那是因为监听了页面 beforeunload 事件。

function addOnBeforeUnload(e) {
    FERD_NavNotice.notification.close();
}
if(window.attachEvent){
    window.attachEvent('onbeforeunload', addOnBeforeUnload);
} else {
    window.addEventListener('beforeunload', addOnBeforeUnload, false);
}

兼容

说到兼容,自然是倒下一大片,而且各浏览器的表现也会有点差异。移动端的几乎全倒,PC端的还好大多都能支持,除了IE。所以使用前,需要先检查一下浏览器是否支持 Notification。

Posted in 未分类, 精彩内容 | HTML5 桌面通知:Notification API已关闭评论
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=0, minimum-scale=1.0, maximum-scale=1.0">
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="black">
<title>html5+css3实现上拉和下拉刷新</title>

<script type="text/javascript" src="http://statics.webkfa.com/js/iscroll.js"></script>

<script type="text/javascript">

var myScroll,
	pullDownEl, pullDownOffset,
	pullUpEl, pullUpOffset,
	generatedCount = 0;

function pullDownAction () {
	setTimeout(function () {	// <-- Simulate network congestion, remove setTimeout from production!
		var el, li, i;
		el = document.getElementById('thelist');

		for (i=0; i<3; i++) {
			li = document.createElement('li');
			li.innerText = 'Generated row ' + (++generatedCount);
			el.insertBefore(li, el.childNodes[0]);
		}
		
		myScroll.refresh();		// Remember to refresh when contents are loaded (ie: on ajax completion)
	}, 1000);	// <-- Simulate network congestion, remove setTimeout from production!
}

function pullUpAction () {
	setTimeout(function () {	// <-- Simulate network congestion, remove setTimeout from production!
		var el, li, i;
		el = document.getElementById('thelist');

		for (i=0; i<3; i++) {
			li = document.createElement('li');
			li.innerText = 'Generated row ' + (++generatedCount);
			el.appendChild(li, el.childNodes[0]);
		}
		
		myScroll.refresh();		// Remember to refresh when contents are loaded (ie: on ajax completion)
	}, 1000);	// <-- Simulate network congestion, remove setTimeout from production!
}

function loaded() {
	pullDownEl = document.getElementById('pullDown');
	pullDownOffset = pullDownEl.offsetHeight;
	pullUpEl = document.getElementById('pullUp');	
	pullUpOffset = pullUpEl.offsetHeight;
	
	myScroll = new iScroll('wrapper', {
		useTransition: true,
		topOffset: pullDownOffset,
		onRefresh: function () {
			if (pullDownEl.className.match('loading')) {
				pullDownEl.className = '';
				pullDownEl.querySelector('.pullDownLabel').innerHTML = 'Pull down to refresh...';
			} else if (pullUpEl.className.match('loading')) {
				pullUpEl.className = '';
				pullUpEl.querySelector('.pullUpLabel').innerHTML = 'Pull up to load more...';
			}
		},
		onScrollMove: function () {
			if (this.y > 5 && !pullDownEl.className.match('flip')) {
				pullDownEl.className = 'flip';
				pullDownEl.querySelector('.pullDownLabel').innerHTML = 'Release to refresh...';
				this.minScrollY = 0;
			} else if (this.y < 5 && pullDownEl.className.match('flip')) {
				pullDownEl.className = '';
				pullDownEl.querySelector('.pullDownLabel').innerHTML = 'Pull down to refresh...';
				this.minScrollY = -pullDownOffset;
			} else if (this.y < (this.maxScrollY - 5) && !pullUpEl.className.match('flip')) {
				pullUpEl.className = 'flip';
				pullUpEl.querySelector('.pullUpLabel').innerHTML = 'Release to refresh...';
				this.maxScrollY = this.maxScrollY;
			} else if (this.y > (this.maxScrollY + 5) && pullUpEl.className.match('flip')) {
				pullUpEl.className = '';
				pullUpEl.querySelector('.pullUpLabel').innerHTML = 'Pull up to load more...';
				this.maxScrollY = pullUpOffset;
			}
		},
		onScrollEnd: function () {
			if (pullDownEl.className.match('flip')) {
				pullDownEl.className = 'loading';
				pullDownEl.querySelector('.pullDownLabel').innerHTML = 'Loading...';				
				pullDownAction();	// Execute custom function (ajax call?)
			} else if (pullUpEl.className.match('flip')) {
				pullUpEl.className = 'loading';
				pullUpEl.querySelector('.pullUpLabel').innerHTML = 'Loading...';				
				pullUpAction();	// Execute custom function (ajax call?)
			}
		}
	});
	
	setTimeout(function () { document.getElementById('wrapper').style.left = '0'; }, 800);
}

document.addEventListener('touchmove', function (e) { e.preventDefault(); }, false);

document.addEventListener('DOMContentLoaded', function () { setTimeout(loaded, 200); }, false);
</script>

<style type="text/css" media="all">
body,ul,li {
	padding:0;
	margin:0;
	border:0;
}

body {
	font-size:12px;
	-webkit-user-select:none;
    -webkit-text-size-adjust:none;
	font-family:helvetica;
}

#header {
	position:absolute; z-index:2;
	top:0; left:0;
	width:100%;
	height:45px;
	line-height:45px;
	background-color:#d51875;
	background-image:-webkit-gradient(linear, 0 0, 0 100%, color-stop(0, #fe96c9), color-stop(0.05, #d51875), color-stop(1, #7b0a2e));
	background-image:-moz-linear-gradient(top, #fe96c9, #d51875 5%, #7b0a2e);
	background-image:-o-linear-gradient(top, #fe96c9, #d51875 5%, #7b0a2e);
	padding:0;
	color:#eee;
	font-size:20px;
	text-align:center;
}

#header a {
	color:#f3f3f3;
	text-decoration:none;
	font-weight:bold;
	text-shadow:0 -1px 0 rgba(0,0,0,0.5);
}

#footer {
	position:absolute; z-index:2;
	bottom:0; left:0;
	width:100%;
	height:48px;
	background-color:#222;
	background-image:-webkit-gradient(linear, 0 0, 0 100%, color-stop(0, #999), color-stop(0.02, #666), color-stop(1, #222));
	background-image:-moz-linear-gradient(top, #999, #666 2%, #222);
	background-image:-o-linear-gradient(top, #999, #666 2%, #222);
	padding:0;
	border-top:1px solid #444;
}

#wrapper {
	position:absolute; z-index:1;
	top:45px; bottom:48px; left:-9999px;
	width:100%;
	background:#aaa;
	overflow:auto;
}

#scroller {
	position:absolute; z-index:1;
/*	-webkit-touch-callout:none;*/
	-webkit-tap-highlight-color:rgba(0,0,0,0);
	width:100%;
	padding:0;
}

#scroller ul {
	list-style:none;
	padding:0;
	margin:0;
	width:100%;
	text-align:left;
}

#scroller li {
	padding:0 10px;
	height:40px;
	line-height:40px;
	border-bottom:1px solid #ccc;
	border-top:1px solid #fff;
	background-color:#fafafa;
	font-size:14px;
}

#myFrame {
	position:absolute;
	top:0; left:0;
}



/**
 *
 * Pull down styles
 *
 */
#pullDown, #pullUp {
	background:#fff;
	height:40px;
	line-height:40px;
	padding:5px 10px;
	border-bottom:1px solid #ccc;
	font-weight:bold;
	font-size:14px;
	color:#888;
}
#pullDown .pullDownIcon, #pullUp .pullUpIcon  {
	display:block; float:left;
	width:40px; height:40px;
	background:url(http://statics.webkfa.com/img/pull-icon@2x.png) 0 0 no-repeat;
	-webkit-background-size:40px 80px; background-size:40px 80px;
	-webkit-transition-property:-webkit-transform;
	-webkit-transition-duration:250ms;	
}
#pullDown .pullDownIcon {
	-webkit-transform:rotate(0deg) translateZ(0);
}
#pullUp .pullUpIcon  {
	-webkit-transform:rotate(-180deg) translateZ(0);
}

#pullDown.flip .pullDownIcon {
	-webkit-transform:rotate(-180deg) translateZ(0);
}

#pullUp.flip .pullUpIcon {
	-webkit-transform:rotate(0deg) translateZ(0);
}

#pullDown.loading .pullDownIcon, #pullUp.loading .pullUpIcon {
	background-position:0 100%;
	-webkit-transform:rotate(0deg) translateZ(0);
	-webkit-transition-duration:0ms;

	-webkit-animation-name:loading;
	-webkit-animation-duration:2s;
	-webkit-animation-iteration-count:infinite;
	-webkit-animation-timing-function:linear;
}

@-webkit-keyframes loading {
	from { -webkit-transform:rotate(0deg) translateZ(0); }
	to { -webkit-transform:rotate(360deg) translateZ(0); }
}

</style>
</head>
<body>

<div id="header"><a href="http://cubiq.org/iscroll">iScroll</a></div>
<div id="wrapper">
	<div id="scroller">
		<div id="pullDown">
			<span class="pullDownIcon"></span><span class="pullDownLabel">Pull down to refresh...</span>
		</div>

		<ul id="thelist">
			<li>Pretty row 1</li>
			<li>Pretty row 2</li>
			<li>Pretty row 3</li>
			<li>Pretty row 4</li>
			<li>Pretty row 5</li>
			<li>Pretty row 6</li>
			<li>Pretty row 7</li>
			<li>Pretty row 8</li>
			<li>Pretty row 9</li>
			<li>Pretty row 10</li>
			<li>Pretty row 11</li>
			<li>Pretty row 12</li>
			<li>Pretty row 13</li>
			<li>Pretty row 14</li>
			<li>Pretty row 15</li>
			<li>Pretty row 16</li>
			<li>Pretty row 17</li>
			<li>Pretty row 18</li>
			<li>Pretty row 19</li>
			<li>Pretty row 20</li>
			<li>Pretty row 21</li>
			<li>Pretty row 22</li>
			<li>Pretty row 23</li>
			<li>Pretty row 24</li>
			<li>Pretty row 25</li>
			<li>Pretty row 26</li>
			<li>Pretty row 27</li>
			<li>Pretty row 28</li>
			<li>Pretty row 29</li>
			<li>Pretty row 30</li>
			<li>Pretty row 31</li>
			<li>Pretty row 32</li>
			<li>Pretty row 33</li>
			<li>Pretty row 34</li>
			<li>Pretty row 35</li>
			<li>Pretty row 36</li>
			<li>Pretty row 37</li>
			<li>Pretty row 38</li>
			<li>Pretty row 39</li>
			<li>Pretty row 40</li>
		</ul>
		<div id="pullUp">
			<span class="pullUpIcon"></span><span class="pullUpLabel">Pull up to refresh...</span>
		</div>
	</div>
</div>
<div id="footer"></div>

</body>
</html>

Posted in 精彩内容 | html5+css3实现上拉和下拉刷新已关闭评论

兼容ie10和ie11的版本判断

function isIe(){
   return ("ActiveXObject" in window);
}
判断IE兼容到IE9

今天一个项目中需要判断IE版本号,又因为 jQuery 2.0 去除了对浏览器版本号的判断(它推荐特性检测),于是就看到一老外写的一段代码:

var _IE = (function(){
    var v = 3, div = document.createElement('div'), all = div.getElementsByTagName('i');
    while (
        div.innerHTML = '<!--[if gt IE ' + (++v) + ']><i></i><![endif]-->',
        all[0]
    );
    return v > 4 ? v : false ;
}());

这段代码着实巧妙!既简介、有向后兼容!一般做法都是:正则搜索 USER_AGENT ;

但因为历史原因, USER_AGENT 一直都不准确,而且被各大厂商改的乱七八糟。

比如:
IE10 : Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.2; WOW64; Trident/6.0)
IE11 : Mozilla/5.0 (Windows NT 6.3; Trident/7.0; rv 11.0) like Gecko
这都是些什么乱七八糟的什么字符串啊。。。鬼知道 IE12会变成什么!!!
所以使用特性检测、或者USER-AGENT检测既不可靠,也不向后兼容!

强烈推荐上述代码!

——————————————————————-

以前刚学会编程的时候,感觉自己知道的挺多,有很多东西可以写;
可真正深入学习很多知识之后,却痛苦的发现自己知道的实在是太少了!

判断IE兼容到IE11

IE浏览器与非IE浏览器的区别是IE浏览器支持ActiveXObject,但是非IE浏览器不支持ActiveXObject。在IE11浏览器还没出现的时候我们判断IE和非IE经常是这么写的

function isIe(){
    return window.ActiveXObject ? true : false;
}

但是在IE11中上面判断的返回的是false,我自己在IE11测试了下如下代码

alert(window.ActiveXObject);
alert(typeof window.ActiveXObject);

用 javascript 判断 IE 版本号支持IE6,IE7,IE8,IE9,IE10,IE11 这是为什么呢?明明ActiveXObject是存在的,怎么就typeof的结果确实undefined。哪位知道结果的告诉我这是为什么呢?为神马?
微软上的官网说出了IE11的ActiveXObject的不同。http://msdn.microsoft.com/en-us/library/ie/dn423948%28v=vs.85%29.aspx。但是并没有解释typeof的原因。如果我们用下面的代码来检测是可以的

alert("ActiveXObject" in window)//在ie11下返回的是false

这是我又不明白了”ActiveXObject” in window返回的是true,为什么以前判断IE浏览器的代码在IE11中返回的是false?再次求大牛们给个解释。谢谢

下面就直接给出兼容IE11判断IE与非IE浏览器的方法。

function isIe(){
   return ("ActiveXObject" in window);
}

注意前提条件是我们的程序代码中不要覆盖ActiveXObject,应该没有哪个程序这么做吧。呵呵。

判断IE6浏览器

从IE7开始IE是支持XMLHttpRequest对象的,唯独IE6是不支持的。根据这个特性和前面判断IE的函数isIe()我们就知道怎么判断IE6了吧。判断方法如下

function isIe6() {
   // ie6是不支持window.XMLHttpRequest的
   return isIe() && !window.XMLHttpRequest;
}
判断IE7浏览器

因为从IE8开始是支持文档模式的,它支持document.documentMode。IE7是不支持的,但是IE7是支持XMLHttpRequest对象的。判断方法如下

function isIe7() {
    //只有IE8+才支持document.documentMode
    return isIe() && window.XMLHttpRequest && !document.documentMode;
}
判断IE8浏览器

在从IE9开始,微软慢慢的靠近标准,我们把IE678称为非标准浏览器,IE9+与其他如chrome,firefox浏览器称为标准浏览器。两者的区别其中有一个是。大家测试下如下代码。返回的是什么

alert(-[1,]);//在IE678中打印的是NaN,但是在标准浏览器打印的是-1

那么我们就可以根据上面的区别来判断是IE8浏览器。方法如下

function isIe8(){
   // alert(!-[1,])//->IE678返回NaN 所以!NaN为true 标准浏览器返回-1 所以!-1为false
   return isIe() &&!-[1,]&&document.documentMode;
}
判断IE9、IE10、IE11浏览器

从IE8浏览器是支持JSON内置对象的,从IE10开始支持js的严格模式,关于JS中的严格模式请参考这篇文章http://www.ruanyifeng.com/blog/2013/01/javascript_strict_mode.html
IE9+下alert(!-[1,])返回的是false,IE9+是支持addEventListener的,但是IE11浏览器中是不支持原来IE中独有的事件绑定attachEvent。根据这些区别我们就能区分出IE9、IE10、IE11浏览器了。

判断其他浏览器
//检测函数
var check = function(r) {
    return r.test(navigator.userAgent.toLowerCase());
};
var statics = {
    /**
    * 是否为webkit内核的浏览器
    */
    isWebkit : function() {
        return check(/webkit/);
    },
    /**
    * 是否为火狐浏览器
    */
    isFirefox : function() {
        return check(/firefox/);
    },
    /**
    * 是否为谷歌浏览器
    */
    isChrome : function() {
        return !statics.isOpera() && check(/chrome/);
    },
    /**
    * 是否为Opera浏览器
    */
    isOpera : function() {
        return check(/opr/);
    },
    /**
    * 检测是否为Safari浏览器
    */
    isSafari : function() {
    // google chrome浏览器中也包含了safari
        return !statics.isChrome() && !statics.isOpera() && check(/safari/)
    }
};
Posted in 精彩内容 | 用 Javascript 判断 IE 版本号支持IE6,IE7,IE8,IE9,IE10,IE11已关闭评论
使用jquery的继承机制 对jquery 1.11.1版本 进行扩展 使其支持 $.browser 方法,已达到兼容之前组件的目的.
jQuery.extend({  
    browser: function()   
    {  
        var  
        rwebkit = /(webkit)\/([\w.]+)/,  
        ropera = /(opera)(?:.*version)?[ \/]([\w.]+)/,  
        rmsie = /(msie) ([\w.]+)/,  
        rmozilla = /(mozilla)(?:.*? rv:([\w.]+))?/,      
        browser = {},  
        ua = window.navigator.userAgent,  
        browserMatch = uaMatch(ua);  
  
        if (browserMatch.browser) {  
            browser[browserMatch.browser] = true;  
            browser.version = browserMatch.version;  
        }  
        return { browser: browser };  
    },  
});  
  
function uaMatch(ua)   
{  
        ua = ua.toLowerCase();  
  
        var match = rwebkit.exec(ua)  
                    || ropera.exec(ua)  
                    || rmsie.exec(ua)  
                    || ua.indexOf("compatible") < 0 && rmozilla.exec(ua)  
                    || [];  
  
        return {  
            browser : match[1] || "",  
            version : match[2] || "0"  
        };  
}  
将以上代码 保存成 jquery-browser.js 使用即可
Tags: ,
Posted in 精彩内容 | Jquery 1.9.0 以上版本 扩展使用 $.browser 方法[转载]已关闭评论

本文参考了 http://www.zhangxinxu.com/wordpress/2015/01/chrome-absolute-display-visibility-render-bug/

在chrome中的样式是一个div 里面有两个子元素,含有float,并且含有position: absolute属性,其他浏览器都正常,
但在Chrome下,来回改变浏览器尺寸的时候,会出现浮动内容重影,位置不对的情况。参考了上面得文章,发现原来chrome一直存在着这么一个bug,
上文中为渲染表现BUG,我们先这面叫,到了大家最关心的地方,如何Hack这个问题,只要在你浮动的子元素或父元素上添加以下CSS即可解决。

-webkit-transform: translateZ(0); /* 没错,靠的就是你!*/
Posted in 精彩内容 | Chrome absolute绝对定位的BUG,导致浮动内容重影,位置不对已关闭评论
2017 08/02 周三

解密JavaScript闭包

为了保证可读性,本文采用意译而非直译。另外,本文版权归原作者所有,翻译仅用于学习。
对于JavaScript新手来说,闭包(Closures)是一个很神奇的东西。这篇博客将通过一个非常浅显的代码示例来解释闭包。

计数器

我们的目标是实现一个计数器,它的效果如下:

increment();  // Number of events: 1
increment();  // Number of events: 2
increment();  // Number of events: 3

可知,每次执行increment()都会输出“Number of events: N”,且N每次都会加1。

这个计数器最直观的实现方式如下:

var counter = 0;
function increment() 
{
  counter = counter + 1;
  console.log("Number of events: " + counter);
}

多个计数器

以上的代码非常简单。但是,当我们需要第二个计数器时,就会遇到问题了。当然,我们可以实现两个重复的计数器:

var counter1 = 0;
function incrementCounter1() 
{
  counter1 = counter1 + 1;
  console.log("Number of events: " + counter1);
}
var counter2 = 0;
function incrementCounter2() 
{
  counter2 = counter2 + 1;
  console.log("Number of events: " + counter2);
}
incrementCounter1();  // Number of events: 1
incrementCounter2();  // Number of events: 1
incrementCounter1();  // Number of events: 2

显然,以上的代码非常冗余,有待优化。当我们需要更多计数器时,使用这种方法将不太现实。这时,就需要神奇的闭包了。

使用闭包实现计数器

需要多个计数器,同时希望去除冗余代码的话,就可以使用闭包了:

function createCounter() 
{
  var counter = 0;
  function increment() 
  {
    counter = counter + 1;
    console.log("Number of events: " + counter);
  }
  return increment;
}
var counter1 = createCounter();
var counter2 = createCounter();
counter1(); // Number of events: 1
counter1(); // Number of events: 2
counter2(); // Number of events: 1
counter1(); // Number of events: 3

在代码中,我们创建了两个独立的计数器counter1与counter2,分别进行计数,互不干挠。代码看着有点奇怪,我们不妨拆分起来分析。

首先,我们来看看createCounter:
创建了一个局部变量counter
创建了一个局部函数increment(),它可以对counter变量进行加1操作。
将局部函数increment()返回。注意,返回的是函数本身,而不是函数调用的结果。
看起来,createCounter()函数与我们最初定义的计数器非常相似。唯一的不同点在于:createCounter()将计数器封装在一个函数内,于是我们将它称作闭包。

难以理解的一点在于,当我们使用createCounter()函数创建计数器时,实际上创建了一个新的函数:

// fancyNewCounter是一个新创建的函数
var fancyNewCounter = createCounter();

闭包的神奇之处在于。每次使用createCounter()函数创建计数器increment时,都会创建一个对应的counter变量。并且,返回的increment函数会始终记住counter变量。

更重要的是,这个counter变量是相互独立的。比如,当我们创建2个计数器时,每个计数器都会创建一个新的counter变量:

// 每个计数器都会从1开始计数
var counter1 = createCounter();
counter1(); // Number of events: 1
counter1(); // Number of events: 2
// 第1个计数器不会影响第2个计数器
var counter2 = createCounter();
counter2(); // Number of events: 1
// 第2个计数器不会影响第1个计数器
counter1(); // Number of events: 3

为计数器命名

多个计数器的输出信息都是“Number of events: N”,这样容易混淆。如果可以为每个计数器命名,则更加方便:

var catCounter = createCounter("cats");
var dogCounter = createCounter("dogs");
catCounter(); // Number of cats: 1
catCounter(); // Number of cats: 2
dogCounter(); // Number of dogs: 1

通过给createCounter传递一个新的counterName参数,可以很容易地做到这一点:

function createCounter(counterName) 
{
  var counter = 0;
  function increment() 
  {
    counter = counter + 1;
    console.log("Number of " + counterName + ": " + counter);
  }
  return increment;
}

这样,createCounter()函数返回的计数器将同时记住两个局部变量:counterName与counter。

优化计数器调用方式

按照之前的实现方式,我们通过调用createCounter()函数可以返回一个计数器,直接调用返回的计数器就可以加1,这样做并不直观。如果可以如下调用将更好:

var dogCounter = createCounter("dogs");
dogCounter.increment(); // Number of dogs: 1

实现代码:

function createCounter(counterName) 
{
  var counter = 0;
  function increment() 
  {
    counter = counter + 1;
    console.log("Number of " + counterName + ": " + counter);
  };
  return { increment : increment };
}

可知,以上的代码返回了一个对象,这个对象包含了一个increment方法。

添加decrement方法

现在,我们可以给计数器添加一个decrement()方法

function createCounter(counterName) 
{
  var counter = 0;
  function increment() 
  {
    counter = counter + 1;
    console.log("Number of " + counterName + ": " + counter);
  };
  function decrement() 
  {
    counter = counter - 1;
    console.log("Number of " + counterName + ": " + counter);
  };
  return {
    increment : increment,
    decrement : decrement
  };
}
var dogsCounter = createCounter("dogs");
dogsCounter.increment(); // Number of dogs: 1
dogsCounter.increment(); // Number of dogs: 2
dogsCounter.decrement(); // Number of dogs: 1

添加私有方法

前面的代码有两行重复的代码,即console.log语句。因此,我们可以创建一个display()方法用于打印counter的值:

function createCounter(counterName) 
{
  var counter = 0;
  function display() 
  {
    console.log("Number of " + counterName + ": " + counter);
  }
  function increment() 
  {
    counter = counter + 1;
    display();
  };
  function decrement() 
  {
    counter = counter - 1;
    display();
  };
  return {
    increment : increment,
    decrement : decrement
  };
}
var dogsCounter = createCounter("dogs");
dogsCounter.increment(); // Number of dogs: 1
dogsCounter.increment(); // Number of dogs: 2
dogsCounter.decrement(); // Number of dogs: 1

看起来,display()函数与increment()函数以及decrement()函数差不多,但是其实它们很不一样。我们并没有将display()函数添加到返回的对象中,这就意味着以下代码会出错:

var dogsCounter = createCounter("dogs");
dogsCounter.display(); // ERROR !!!

这时,display()相当于一个私有方法,我们只能在createCounter()函数内使用它。

闭包与面向对象编程

如果你接触过面向对象编程(OOP),则应该不难发现本文中所涉及的内容与OOP中的类、对象、对象属性、共有方法与私有方法等概念非常相似。
闭包,与OOP相似,就是把数据和操作数据的方法绑定起来。因此,在需要OOP的时候,就可以使用闭包来实现。

总结

闭包(Closure)是JavaScript一个非常棒的特性。掌握它,我们可以从容应对一些常见的编程需求。

版权声明: 转载时请注明作者Fundebug以及本文地址: https://blog.fundebug.com/2017/07/31/javascript-closure/
Posted in 精彩内容 | 解密JavaScript闭包已关闭评论

一般讲的宽度指的是内容宽度,但一个 div 的实际宽度不仅只于内容宽度,尤其在做 CSS 排版时更不能搞错,必须同时考虑 Padding、Border 与 Margin 的宽度,四个加起来才是 div 真正占有的宽度。
JQUERY 获取 DIV 宽度与高度(width,padding,margin,border)全都有了!

var content = $(‘div’). width();//只是获取content宽度
var contentWithPadding = $(‘div’). innerWidth();//获取content+padding的宽度
var withoutMargin = $(‘div’). outerWidth();//获取content+padding+border的宽度
var full = $(‘div’). outerWidth(true);//获取content+padding+border+margin的宽度

宽度与高度概念一样,只差水平与垂直向而已。

Margin 有个特色,就是当两个有 margin的 div 靠在一起时,两个 div 紧邻的 margin 不是相加,而是取其大者,举例来说,两个 10px margin 的 div 并在一起只会有 10px margin 的距离,若是一个 20px margin,另一个为 10px margin,则会有 20px 的 margin。

但是获取当前元素的全部宽度的时候,还是获取自身的margin+content+padding+border的值。
Tags:
Posted in 精彩内容 | jQuery 获取 div 宽度与高度(width,padding,margin,border)已关闭评论
2017 06/21 周三

Atom 各种插件

Atom是一款最近非常流行的编辑软件,除了免费,他还有很多的插件为用户提供更好的服务。
这里介绍几款大家都用的到的插件

在设置 → 安装扩展 输入框中输入以下关键字,就可以找到相应的插件
1. tag 作者: uzyn 此时版本0.5.0 功能: 自动添加关闭tag, 如输入<div>, 当输入</会自动关闭div
2. Activate Power Mode 作者: JoelBesada 此时版本2.0.0 功能:打字特效
Posted in 精彩内容 | Atom 各种插件已关闭评论
此方法适用于Excel2007以后的版本

利用条件格式来找出表中不一样的部分

对表1和表2进行比较,如下图

1. 选择需要比较的单元格,单击条件格式

选择里面的新建规则

2. 选择“使用公式确定要设置格式的单元格”,并输入公式

在格式处,设置结果所显示的样式。点击确定 这时候,表2与表1不同的部分将被你设置的样式所代替。
Tags:
Posted in 精彩内容 | Excel 比较两个表,给内容不同的单元格添加颜色已关闭评论