Tag Archives: javascript

Hello World

玩死浏览器的n种方法 (1)—-动态dom集合导致的无限循环

来看看这段简单的dom操作,clone子节点操作代码:将div内的子节点挨个clone一次,然后再插入这个div内

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<body>
<a href="javascript:void(0);" id="start">start</a>
<div id="test">
  <div>t1</div>
  <div>t2</div>
</div>
<script type="text/javascript">
window.onload = function(){
  document.getElementById('start').onclick = function(){
	var div = document.getElementById('test'),
	  childs = div.getElementsByTagName('div');
	for(var i = 0; i < childs.length; i++){
	  var d = childs[i].cloneNode(true);
	  div.appendChild(d);
	}
  };
};
</script>
</body>

试着执行一下,你会发现,浏览器卡死了 🙁
为什么哪?
因为通过getElementsByTagName取得的节点集合childs是动态的,当继续往父节点div内添加子节点时,也同时将这些节点添加到了childs的里面~~所以循环变成了无限的
对于这个例子,你可以在循环前将将childs.length保存到一个变量里面,然后在循环时,将i与这个变量比较
当然,这样并非真的解决了问题,比如你如果是要通过insertBefore插入节点,即插入的节点不是在已有的节点后面,上面虽然不会死循环,但循环仍然会乱掉,原因很简单:childs是动态的
正切的做法是重新定义一个childs数组,让后将原来的DOM对象childs里的节点,放到js数组childs里面,这时的childs就不再是动态的了

Hello World 业界杂谈

巧用setTimeout提高UI响应速度

关于javascript的性能问题,争议最多的就是它的单线程:UI渲染与更新数据跑在一个线程里,如果碰上比较耗时的数据运算或者DOM操作,UI就会很卡,特别是对IE6、7这些古董级的东西,这个问题会特别明显。
这里不想分辨单线程到底是优点还是缺点,只想所说如何通过setTimeout规避这种问题。
比如本博客上面红色的导航条(嘿嘿,换了皮肤,这个还没加),当滚动条向下滚动时,通过修改css的position值,菜单条会变成悬浮在页面顶端的,在页面滚动过程中,js会不断检查当前滚动条的位置,直到导航条回到初始位置时,再将其定位回该位置。
由于要不停的访问DOM,取得滚动条的高度,这段代码在IE6、IE7里是很卡的,以至于刚开始的时候,我在这段逻辑的开头加了浏览器类型的判断,如果是IE,干脆不再执行这段代码~~~直到加入了下面这段优化以后

1
2
3
4
5
6
7
8
var scroolTime = null;
$(window).bind('scroll.top', function(){
  scroolTime &amp;&amp; clearTimeout(scroolTime);
  scroolTime = setTimeout(scroolFun, 0);
});
var scroolFun = function(){
  //.....检查页面高度、设置菜单条css的逻辑
}

上面这段代码的核心就是“setTimeout(xxxx, 0);”,咋一看,定时为0不是等于没定时嘛,其实不然,即使定位为0,setTimeout也不会立即执行定时的function,而是在当前线程空闲时才会执行,也就是说,     随着滚动条的滚动,js逻辑会等喜面的代码跑完,且ui渲染完了以后再去调用     scroolFun。而“clearTimeout(scroolTime)”会保证定时队列里只有一个待执行的scroolFun

再比如,以前做的的一个需求,在js内上有一个几十到上千条的数组,数组内保存的通讯录的实体,页面上要求可以对通讯录做搜索。如果照正常的逻辑,需要将整个数组跑一遍循环,再将符合条件的结果集显示到页面上。特别是在做搜索的时候,需要支持随着用户输入的时时搜索,这种情况下,如果数组长度过千,即便是chrome这样的高端浏览器,页面操作仍然不回很流畅。
这是又到了setTimeout派上用场的时候了:每次对数组搜索时,只搜索10到100条不等的一段,搜索完这一段即将满足条件的结果显示到页面上,然后setTimeout(‘分段搜索function(start, length)’, 0),等待线程空闲以后,再继续搜索下一段。
然后在用户修改搜索关键字时,记得要将上面的定时clearTimeout掉。
具体每次的搜索长度,可以根据浏览器不同来定义,不如IE6,每次只能搜索10条,而chrome每次搜索几百条。
—————-

Hello World 他山石 点点滴滴

你真的会用javascript吗?

偶然在csdn看到几个js的小题,考察的都是很基础的知识,拿来分享一下
1.

1
2
3
4
if (!("a" in window)) {
  var a = 1;
}
alert(a);

开始,我以为是1,后来试了下,结果是undefined,仔细看看,也对:js在执行是,会首先提取所有var的新变量,让后给其赋值undefined,并将其加入到当前的执行环境总,比如这里,程序还没执行,实际就有了window.a=undefined,所以“(“a” in window)==true”
2.

1
2
3
4
5
var a = 1;
var b = function a(x) {
  return x*x;
};
alert(a);

一直以为这种写法会报错,汗颜的是,前段时间去某家公司面试,还碰上了类似的题目,回来也没写写试试。
实际试了下,这里的alert仍然是1,这种写法,a只在function a的内部是function,但是如果在a内部调用a(x),会递归
3.

1
2
3
4
5
function a(x) {
    return x * 2;
}
var a;
alert(a);

呵呵,有了第一个题,这个就比较简单了,function
4.

1
2
3
4
5
function b(x, y, a) {
    arguments[2] = 10;
    alert(a);
}
b(1, 2, 3);

这个也简单,arguments[2]就是a的替身,a会被修改为10
5.

1
2
3
4
function a() {
    alert(this);
}
a.call(null);

这里考察的是call的使用,正常call的参数是什么,里面的this就是什么(我也是这么理解的),但是关于call还有一条说明:如果call的参数是null或者undefined,里面的this仍然是当前执行环境,即window
————-
参考:http://bbs.csdn.net/topics/390300541

Hello World 业界杂谈

eclipse设置空格代替tab

随着node.js的流行,js的书写方式正在悄然发生变化。
node.js里异步编程的大量使用,回调越来越多,缩进也就越来越多,以前,习惯了tab/四个格的缩进,现在发现这种缩进方式对代码越多影响越来越大:缩进太多,内层的代码要靠拖动水平滚动条才能看到;另外,tab键在各个IDE里显示的空格个数是不同的,2~8个不等。
所以,就准备修改自己js的书写习惯:用两个空格代替tab。
但是,如果真的手敲空格又缺失太麻烦,还好大部分IDE都会提供使用空格代替tab的的功能,即敲tab后,实际产生的是另个空格。
下面介绍一下eclipse的设置方式,如下图,首先打开eclipse的“偏好设置”,找到图中所示的选项:

在Formatter找到“Active profile”,不要直接修改系统默认的profile,点下面的New,新建一个,然后打开,如下图所示:

在第一个选项卡里找到General settings -> tab policy,右侧的下拉改为“Spaces only”,下面的两个“size”都改成2,保存推出即可
———

Hello World

seajs入门

seajs是一个功能强大、使用简单的js包管理器,有了它,就可以方便快捷的实现js的模块化编程
下面简单介绍seajs的使用方法
比如有index.html,加入如下代码即可引入seajs及相关的配置:

<script src="assets/js/sea.js"></script>
<script>
  seajs.config({
    alias: {
      // 指定使用的 jQuery 版本 注意:这里的jauery是经过seajs包装的
      'jquery': 'jquery-1.8.0' 
    }
  })
  // 加载入口模块,main.js
  seajs.use('./assets/js/main')
</script>

在main.js内:

  define(function(require){
    var SS = require('./ss');
    var s = new SS();
    s.fun1(); //"fun1"
  });

上面通过require引入了一个叫做ss的模块,如下:ss.js

define(function(require, exports, module){
  //引入jquery模块
  var $ = require('jquery');
  //对外输入SS类
  module.exports = function(){
    function SS(){
 
    }
    SS.prototype = {
      fun1 : function(){
        alert('fun1');
      }
    };
    return SS;
  }();
});

关于seajs的简单使用,上面就讲完了,最后在说一个小细节:通过require(‘xxx’)引入的对象是“单例”的,比如下面s2.js

define(function(require, exports, module){
  module.exports = function(){
    function SS(){
      this.s = 1;
    }
    SS.prototype = {
      fun1 : function(){
        alert(this.s);
      }
    };
    //返回实例化后的SS
    return new SS();
  }();
});

再这样调用s2

  var t1 = require('./s2');
  t1.s = 3;
  var t2 = require('./s2');
  t2.fun1(); //这是,会alert 3

看到了吧,s2本来有一个属性s=1,再第一次引用t1内,被设置为3,再次通过require引用s2到t2,此时t2的s属性也变成了3,也就是说在seajs内有一个“require”的对象库,每一个通过module.exports输出的对象都会被保存在里面,再次require时,返回到仍然是原对象
————-
关于seajs更多内容,请参考:https://github.com/seajs/seajs/issues/266

Hello World 业界杂谈 他山石

[转]来自浏览器创造者的web优化技巧

Jatinder Mann是微软Internet Explorer产品的一名项目经理,在BUILD 2012大会上,他做了题为“提高HTML5应用和网站性能的50条秘技(50 performance tricks to make your HTML5 apps and sites faster)”的演讲,介绍了很多为Web应用提速的技巧。

Mann的建议是按照下面六个原则组织的。

1. 快速响应网络请求

  • 避免重定向。排名前1000的网站中,63%使用了重定向。如果不执行重定向的话,页面速度可以提高10%。
  • 避免Meta-refresh。世界上14%的URL使用了Meta-refresh。
  • 尽可能通过CDN定位用户,使服务器响应时间最小化。
  • 从不同的域下载资源,使并发连接的应用最大化。
  • 复用连接。不要在响应请求时关闭连接。
  • 确保页面加载不会因合作伙伴网站提供的数据而延迟。
  • 了解耗时的网络组件,如重定向、缓存、DNS、请求和响应等。在IE 9和10中可以使用Navigation Timing API来测量浏览器花在每个操作上的时间。

2. 最小化下载的字节数

  • 加载页面时,要尽量减少下载的数据量。平均而言,每个页面要下载的数据量达777KB,其中有474KB的图片、128KB的脚本和84KB的Flash。
  • 请求gzip压缩的内容。
  • 将资源保存在本地的包中,比如为Windows商店应用生成的包资源索引(Package Resource Index)文件。这样当需要这些资源时就可以很容易地获取到。
  • 使用HTML5 App Cache缓存动态资源。App Cache会只下载一次资源,从而避免多次网络行程。当应用的版本号发生变化时,它会自动重新下载相应资源。
  • 尽量在响应中使用“Expires”字段来提供可缓存的内容。
  • 通过设定请求的If-Modified-Since字段来使用条件请求。
  • 缓存数据请求,如HTTP、XML和JSON等,因为大约95-96%的请求不会整天变化。虽然这个想法很合理,但实际缓存接收到的请求的网站所占比重还不到1%。
  • 用大写将文件命名标准化。虽然服务器可能把Icon.jpg当作 icon.jpg,但是对于Web平台而言,它们是不同的资源,对应不同的网络请求。

3. 高效地组织标记

  • 对于IE而言,请使用最新的标记标准,因为它速度最快。IE 10也能识别早期的IE6-IE9标记风格,但是其速度不如新的标记风格。
  • 特定的业务Web应用可能需要强制IE运行于传统模式,请使用HTTP头字段“X-UA-Compatible: IE=EmulateIE7”来代替HTML标签 ,这样速度会快一些。
  • 为了平滑地渲染,样式表应该链接在页面顶部的<head>之中的<title>后面。
  • 绝对不要在页面底部链接样式表。否则加载页面时可能会出现闪烁。
  • 对于分层样式,不要使用“@import”,因为它是同步的,会阻塞CSS数据结构的创建和屏幕绘制。
  • 避免样式的嵌入和内联,因为它会强制浏览器在HTML和CSS解析器之间进行上下文切换。
  • 仅包含必要的样式。不要下载和解析用不到的样式。
  • 仅在页面底部链接JavaScript。这可以确保脚本执行时图片和CSS等资源已经加载,无需等待,也避免了上下文切换。
  • 不要在页面开头链接JavaScript。如果某些脚本必须在开始处加载的话,请使用“defer”属性。
  • 不要内联JavaScript,这样可以避免上下文切换。
  • 使用“async”属性加载JavaScript,这样整个脚本就可以异步加载和执行。
  • 避免冗余代码。世界上52%的网页包含100行甚至更多的冗余代码,比如一个JavaScript文件被链接了两次。
  • 将一个JS框架标准化,无论是jQuery,Dojo,Prototype.js还是其他框架。浏览器没有必要加载多个功能基本相同的框架。
  • 不要加载FB和Twitter等网站的脚本,只是看起来很酷而已,它们会争用资源。

4. 优化多媒体资源的使用

  • 图片是最常用的资源,每个页面平均会下载58张图片。
  • 尽量避免下载太多图片,根据页面加载时间将图片最大数量控制在20-30之间。
  • 使用Image Sprites将多个图片组合成一个。该技术可以减少网络连接数,也会减少下载的字节数并节省GPU处理周期。
  • 手动创建Image Sprites,因为工具创建的可能会留下较大的空洞,这会加大需要下载的数据量,也需要更多的GPU 处理周期。
  • 使用PNG格式的图片,该格式在下载大小、解码时间、兼容性和压缩率之间实现了完美的折中。JPEG格式可以用于照片。
  • 使用原始的图像分辨率,这样可以避免下载不必要的数据以及缩放所需的CPU 处理。
  • 尽可能使用CSS3 Gradient替代图片。
  • 尽可能使用CSS3 border radius替代图片。
  • 使用CSS3 Transform来创建移动、旋转和倾斜效果。
  • 对于小型的单个图片,可以使用Data URI。这样可以节省一张图片的下载量。
  • 避免复杂的SVG,因为它们会延长下载和处理时间。
  • 当包含HTML5时,指定一个预览图片。这样浏览器就不必下载整个视频文件来确定预览图片了。
  • 使用HTML5来代替Flash、Silverlight或QuickTime。HTML5速度更快,而且其他几种形式的运行时插件会消耗系统资源。
  • 主动地以异步方式下载富媒体资源并将其保存在App Cache中。

5. 编写快速的JavaScript

  • 在JavaScript中进行数学运算时尽量使用整型。JavaScript的浮点运算比相应的整型运算耗费的时间要多得多。在进行数学运算,特别是计算密集型运算时,请使用Math.floor和Math.ceil将浮点数转换为整型数。
  • 降低JavaScript代码量,这样不但可以减少下载的数据量,而且能够提供更好的运行时性能。
  • 按需初始化JS。当需要时动态加载JS。
  • 通过缓存变量(如document和body等)使DOM交互减到最少。
  • 使用内置的DOM代码,如element.firstChild或node.nextSibling等。这些代码都是高度优化的,相对于第三方库能提供更好的性能。
  • 访问大量DOM元素时,使用querySelectorAll。
  • 使用.innerHTML来构建动态页面。
  • 批量标记更改。
  • 维护小巧而健壮的DOM——将其元素数目控制在1000以内。.
  • JSON快于XML。
  • 使用浏览器的JSON原生方法。
  • 不要滥用正则表达式。

6. 知道你的应用在做什么

  • 理解JavaScript定时器,了解setTimeout和clearInterval。除非确定要使用定时器完成一些功能,否则不要启动定时器。组合定时器也是如此。
  • 如果监视器的刷新率是60Hz,请将显式帧的定时器调整为16.7 ms。
  • 在IE 10、Chrome和Firefox中,图形处理请使用requestAnimationFrame动画函数。其绘制通过回调实现,因此不需要定时器。
  • 使用可见性API(document.hidden和 Visibilityhange)来确定应用程序的可见状态,如果页面是隐藏的,就关闭该活动。这样可以节省CPU和电池寿命。
  • Mann建议在IE中使用Windows Performance Tools来测试Web页面的性能, 并以减少CPU时间和增加并发性为目标进行优化

—————-
转自:http://www.searchsoa.com.cn/showcontent_68434.htm

Hello World

使用jsp输出js

    服务器端将多个js文件整合到一个请求内输出,是一种常用的减少页面请求数的方法;另外,如果js文件间的逻辑是相互依赖的,需要控制它们的加载顺序,在服务器端做文件整合也可以规避这种扯淡的事
    以前php的时候,使用简单的“include”,就可以完成这事,最近做了一个java的项目,也想使用这种方式,就想用jsp的include试试,已经想到了可能会有中文乱码的问题,于是加上了:pageEncoding=”UTF-8″

  <%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
  <%@ page contentType="application/javascript" %>
  <%@ include file="xxx1.js" %>
  <%@ include file="xxx2.js" %>
  <%@ include file="xxx3.js" %>

    结果~~~还乱码 o(╯□╰)o
    查了很多资料,做了N多测试,大概有这么几种
  1. 将文件另存为“utf-8”格式,覆盖到项目里
    —-觉得这是多此一举,直接在eclipse里修改文件属性不就行了:检查文件属性,本来就是utf-8
  2. 在web.xml里做一些设置,让js文件可以做jsp解析
    —-无效
  3. script标签加上“charset”,等效的,jquery ajax加载脚本上,加上“scriptCharset”
    —-折腾了半天,还是无效
  4. 。。。。。
    搞来搞去,还是没有成功,jsp的乱码问题果然名不虚传 +_+

    刚才,本着死马当活马医的精神,拿第一种方法试了试,使用txt编辑器将js代码拷出来,另存为utf-8格式,在覆盖到项目目录内~~ok了 ~\(≧▽≦)/~
    (看来eclipse文件属性里的编码只是说以什么编码方式打开,和文件本身的编码的方式无关)

—————
    btw:两年多没有碰过java了,这次帮别的部门做项目,不得不又开始搞jsp,有了php的类比,深感用jsp/tomcat做web网站有多么麻烦,jstl简直多此一举。更喜欢php的方式:通过“”标签将服务器端逻辑和浏览器端的逻辑完全分开,简洁而不失优雅。反观el/jstl,非要将本来属于java端的逻辑与html混在一起~~~java还是适合做复杂的后台逻辑,然后用类似thrift的东西与web端解耦

Hello World

html5 localStorage使用备忘

localStorage和sessionStorage是html5提出的web存储方案,难能可贵的是,除了chrome/safari/firefox/opera,老大难的IE,从IE8便开始支持它们!所以,今天好好研究了这两个东西:

1. localStorage地持久的,sessionStorage是只对当前页面,也就是“session”的,关闭浏览器、关闭窗口就会失效,甚至在同一个浏览器中再开一个窗口,都不能相互访问。除此之外,二者的表现是一样的

2. 存储容量的大小:html5标准是5M,但具体的,也依照各个浏览器的实际情况(这一点要多加注意)

3. api

window.localStorage.name = 'rainman';           // 赋值
window.localStorage.setItem('name','cnblogs');  // 赋值
window.localStorage.getItem('name');            // 取值
window.localStorage.removeItem('name');         // 移除值
delete window.localStorage.name                 // 移除值
window.localStorage.clear();                    // 删除所有localStorage

4. 在将value付给localStorage时候,会自然的调一下value.toString(),如果value是一个对象,而没有做变换,保存的就是字符串“[object Object]”~~汗吧。所以这里需要用到JSON.stringify()和JSON.parse(),万幸的是支持localStorage,都支持这两个方法(包括IE8)

BTW:其他web存储方式:
Database Storage是html5的“Web SQL Database”API,可以想操作其他关系型数据库一样,使用sql读写~~可惜的是,只有Chrome支持它
————

Hello World 美图酷图

让bootstrap的自动补齐功能支持ajax数据源


如上图,bootstrap的自动补齐功能非常好用($(‘#xxx’).typeahead()),可以自定义筛选、排序方法,对键盘操作的支持也非常好,可惜,美中不足的是。。。它不支持可变数据源,如果提示数据需要通过ajax取得的,就用不了了
无奈,只能改它源码了~~~只要简单的3个小修改

1. 添加updateSource方法

//在Typeahead类的构造方法里,添加:
this.updateSource = this.options.updateSource || false

2. 鼠标输入时,先执行updateSource方法

//找到keyup方法,修改switch的default项:
if(this.updateSource){
  var that = this
  this.updateSource(this.$element.val(), function(source){
    //取得新数据源后的回调
    that.source = source;
    that.lookup()
  });
}else{
  //如果没有配置updateSource,直接执行lookup方法
  this.lookup()
}

3. 修改筛选数据源的方法

//找到lookup方法,然后同时是否配置了updateSource,判断是否执行默认的筛选方法
if(!this.updateSource){
  items = $.grep(this.source, function (item) {
    if (that.matcher(item)) return item
  })
  items = this.sorter(items)
}else{
  items = this.source;
}

ok,大功告成
并且,这里只是“扩展了typeahead方法”,如果使用固定的数据源,还可以照着之前的方式使用,在需要使用ajax数据源的时候:

	$('#input_typeahead').typeahead({
		source : [],
		items : 7,
		highlighter : function(item){
			return item;
		},
		updateSource : function(inputVal, callback){
			$.ajax({
				type : 'POST',
				url : '/test/ajax_search_tip',
				dataType : 'json',//返回需要展示的json数组
				data : {length : 5, input : inputVal},
				success : function(d){
					callback(d);
				}
			});
		}
	});

——-
嘿嘿,顺便发个美图

Hello World

简单实用的js模板引擎

不足50行的js模板引擎,支持各种js语法:

<script id="test_list" type="text/html">
<%=
	for(var i = 0, l = p.list.length; i < l; i++){
		var stu = p.list[i];
=%>
	<tr>
		<td<%=if(i==0){=%> class="first"<%=}=%>><%==stu.name=%></td>
		<td><%==stu.age=%></td>
		<td><%==(stu.address || '')=%></td>
	<tr>
 
<%=
	}
=%>
</script>

“<%= xxx =%>”内是js逻辑代码,“<%== xxx =%>”内是直接输出的变量,类似php的echo的作用。“p”是调用下面build方法时的k-v对象参数,也可以在调用“new JTemp”时设置成别的参数名

调用:

$(function(){
	var temp = new JTemp('test_list'),
		html = temp.build(
			{list:[
           		{name:'张三', age:13, address:'北京'},
        		{name:'李四', age:17, address:'天津'},
        		{name:'王五', age:13}
        	]});
	$('table').html(html);
});

上面的temp生成以后,可以多次调用build方法,生成html

一下是模板引擎的代码:

var JTemp = function(){
	function Temp(htmlId, p){
		p = p || {};//配置信息,大部分情况可以缺省
		this.htmlId = htmlId;
		this.fun;
		this.oName = p.oName || 'p';
		this.TEMP_S = p.tempS || '<%=';
		this.TEMP_E = p.tempE || '=%>';
		this.getFun();
	}
	Temp.prototype = {
		getFun : function(){
			var _ = this,
				str = $('#' + _.htmlId).html();
			if(!str) _.err('error: no temp!!');
			var str_ = 'var ' + _.oName + '=this,f=\'\';',
				s = str.indexOf(_.TEMP_S),
				e = -1,
				p,
				sl = _.TEMP_S.length,
				el = _.TEMP_E.length;
			for(;s >= 0;){
				e = str.indexOf(_.TEMP_E);
				if(e < s) alert(':( ERROR!!');
				str_ += 'f+=\'' + str.substring(0, s) + '\';';
				p = _.trim(str.substring(s+sl, e));
				if(p.indexOf('=') !== 0){//js语句
					str_ += p;
				}else{//普通语句
					str_ += 'f+=' + p.substring(1) + ';';
				}
				str = str.substring(e + el);
				s = str.indexOf(_.TEMP_S);
			}
			str_ += 'f+=\'' + str + '\';';
			str_ = str_.replace(/\n/g, '');//处理换行
			var fs = str_ + 'return f;';
			this.fun = Function(fs);
		},
		build : function(p){
			return this.fun.call(p);
		},
		err : function(s){
			alert(s);
		},
		trim : function(s){
			return s.trim?s.trim():s.replace(/(^\s*)|(\s*$)/g,""); 
		}
	};
	return Temp;
}();

核心是将模板代码转变成了一个拼接字符串的function,每次拿数据call这个function。

因为主要是给手机(webkit)用的,所以没有考虑字符串拼接的效率问题,如果需要给IE实用,最好将字符串拼接方法改为Array.push()的形式