前端是庞大的,包括html、css、javascript、image、flash等等各种各样的资源。前端优化是复杂的,针对方方面面的资源都有不同的方式。那么,前端优化的目的是什么
1. 从用户角度而言,优化能够让页面加载得更快、对用户的操作响应得更及时,能够给用户提供更为友好的体验。
2. 从服务商角度而言,优化能够减少页面请求数、或者减小请求所占带宽,能够节省可观的资源。
总之,恰当的优化不仅能够改善站点的用户体验并且能够节省相当的资源利用。
前端优化的途径有很多,按粒度大致可以分为两类,第一类是页面级别的优化,例如http请求数、脚本的无阻塞加载、内联脚本的位置优化等;第二类则是代码级别的优化,例如javascript中的dom操作优化、css选择符优化、图片优化以及html结构优化等等。另外,本着提高投入产出比的目的,后文提到的各种优化策略大致按照投入产出比从大到小的顺序排列。
一、页面级优化
1. 减少http请求数
这条策略基本上所有前端人都知道,而且也是最重要最有效的。都说要减少http请求,那请求多了到底会怎么样呢?首先,每个请求都是有成本的,既包含时间成本也包含资源成本。一个完整的请求都需要经过dns寻址、与服务器建立连接、发送数据、等待服务器响应、接收数据这样一个”漫长”而复杂的过程。时间成本就是用户需要看到或者”感受”到这个资源是必须要等待这个过程结束的,资源上由于每个请求都需要携带数据,因此每个请求都需要占用带宽。另外,由于浏览器进行并发请求的请求数是有上限的(具体参见此处),因此请求数多了以后,浏览器需要分批进行请求,因此会增加用户的等待时间,会给用户造成站点速度慢这样一个印象,即使可能用户能看到的第一屏的资源都已经请求完了,但是浏览器的进度条会一直存在。
减少http请求数的主要途径包括:
(1). 从设计实现层面简化页面
如果你的页面像百度首页一样简单,那么接下来的规则基本上都用不着了。保持页面简洁、减少资源的使用时最直接的。如果不是这样,你的页面需要华丽的皮肤,则继续阅读下面的内容。
(2). 合理设置http缓存
缓存的力量是强大的,恰当的缓存设置可以大大的减少http请求。以有啊首页为例,当浏览器没有缓存的时候访问一共会发出78个请求,共600多k数据(如图1.1),而当第二次访问即浏览器已缓存之后访问则仅有10个请求,共20多k数据(如图1.2)。(这里需要说明的是,如果直接f5刷新页面的话效果是不一样的,这种情况下请求数还是一样,不过被缓存资源的请求服务器是304响应,只有header没有body,可以节省带宽)
怎样才算合理设置?原则很简单,能缓存越多越好,能缓存越久越好。例如,很少变化的图片资源可以直接通过http header中的expires设置一个很长的过期头;变化不频繁而又可能会变的资源可以使用last-modifed来做请求验证。尽可能的让资源能够在缓存中待得更久。关于http缓存的具体设置和原理此处就不再详述了,有兴趣的可以参考下列文章:
http1.1协议中关于缓存策略的描述
fiddler http performance中关于缓存的介绍
(3). 资源合并与压缩
如果可以的话,尽可能的将外部的脚本、样式进行合并,多个合为一个。另外,css、javascript、image都可以用相应的工具进行压缩,压缩后往往能省下不少空间。
(4). css sprites
合并css图片,减少请求数的又一个好办法。
(5). inline images
使用data: url scheme的方式将图片嵌入到页面或css中,如果不考虑资源管理上的问题的话,不失为一个好办法。如果是嵌入页面的话换来的是增大了页面的体积,而且无法利用浏览器缓存。使用在css中的图片则更为理想一些
(6). lazy load image
这条策略实际上并不一定能减少http请求数,但是却能在某些条件下或者页面刚加载时减少http请求数。对于图片而言,在页面刚加载的时候可以只加载第一屏,当用户继续往后滚屏的时候才加载后续的图片。这样一来,假如用户只对第一屏的内容感兴趣时,那剩余的图片请求就都节省了。有啊首页曾经的做法是在加载的时候把第一屏之后的图片地址缓存在textarea标签中,待用户往下滚屏的时候才”惰性”加载。
2. 将外部脚本置底
前文有谈到,浏览器是可以并发请求的,这一特点使得其能够更快的加载资源,然而外链脚本在加载时却会阻塞其他资源,例如在脚本加载完成之前,它后面的图片、样式以及其他脚本都处于阻塞状态,直到脚本加载完成后才会开始加载。如果将脚本放在比较靠前的位置,则会影响整个页面的加载速度从而影响用户体验。解决这一问题的方法有很多,在这里有比较详细的介绍(这里是译文和更详细的例子),而最简单可依赖的方法就是将脚本尽可能的往后挪,减少对并发下载的影响。
3. 异步执行inline脚本
inline脚本对性能的影响与外部脚本相比,是有过之而无不及。首页,与外部脚本一样,inline脚本在执行的时候一样会阻塞并发请求,除此之外,由于浏览器在页面处理方面是单线程的,当inline脚本在页面渲染之前执行时,页面的渲染工作则会被推迟。简而言之,inline脚本在执行的时候,页面处于空白状态。鉴于以上两点原因,建议将执行时间较长的inline脚本异步执行,异步的方式有很多种,例如使用script元素的defer属性(存在兼容性问题和其他一些问题,例如不能使用document.write)、使用settimeout,此外,在html5中引入了web workers的机制,恰恰可以解决此类问题
4. lazy load javascript
随着javascript框架的流行,越来越多的站点也使用起了框架。不过,一个框架往往包括了很多的功能实现,这些功能并不是每一个页面都需要的,如果下载了不需要的脚本则算得上是一种资源浪费-既浪费了带宽又浪费了执行花费的时间。目前的做法大概有两种,一种是为那些流量特别大的页面专门定制一个专用的mini版框架,另一种则是lazy load。yui则使用了第二种方式,在yui的实现中,最初只加载核心模块,其他模块可以等到需要使用的时候才加载
5. 将css放在head中
如果将css放在其他地方比如body中,则浏览器有可能还未下载和解析到css就已经开始渲染页面了,这就导致页面由无css状态跳转到css状态,用户体验比较糟糕。除此之外,有些浏览器会在css下载完成后才开始渲染页面,如果css放在靠下的位置则会导致浏览器将渲染时间推迟。
6. 异步请求callback
在某些页面中可能存在这样一种需求,需要使用script标签来异步的请求数据。类似:
javascript:
/*callback函数*/
function mycallback(info){
//do something here
}
html:
<script type=”text/javascript” src=”http://abc.com/cb”></script>
cb返回的内容:
mycallback(‘hello world!’);
像以上这种方式直接在页面上写<script>对页面的性能也是有影响的,即增加了页面首次加载的负担,推迟了domloaded和window.onload事件的触发时机。如果时效性允许的话,可以考虑在domloaded事件触发的时候加载,或者使用settimeout方式来灵活的控制加载的时机。
7. 减少不必要的http跳转
对于以目录形式访问的http链接,很多人都会忽略链接最后是否带’/’,假如你的服务器对此是区别对待的话,那么你也需要注意,这其中很可能隐藏了301跳转,增加了多余请求。具体参见下图,其中第一个链接是以无’/’结尾的方式访问的,于是服务器有了一次跳转。
8. 避免重复的资源请求
这种情况主要是由于疏忽或页面由多个模块拼接而成,然后每个模块中请求了同样的资源时,会导致资源的重复请求
二、代码级优化
1. javascript
(1). dom
dom操作应该是脚本中最耗性能的一类操作,例如增加、修改、删除dom元素或者对dom集合进行操作。如果脚本中包含了大量的dom操作则需要注意以下几点:
a. html collection
在脚本中document.images、document.forms、getelementsbytagname()返回的都是htmlcollection类型的集合,在平时使用的时候大多将它作为数组来使用,因为它有length属性,也可以使用索引访问每一个元素。不过在访问性能上则比数组要差很多,原因是这个集合并不是一个静态的结果,它表示的仅仅是一个特定的查询,每次访问该集合时都会重新执行这个查询从而更新查询结果。所谓的”访问集合”包括读取集合的length属性、访问集合中的元素。
因此,当你需要遍历html collection的时候,尽量将它转为数组后再访问,以提高性能。即使不转换为数组,也请尽可能少的访问它,例如在遍历的时候可以将length属性、成员保存到局部变量后再使用局部变量。
b. reflow & repaint
除了上面一点之外,dom操作还需要考虑浏览器的reflow和repaint,因为这些都是需要消耗资源的,具体的可以参加以下文章:
如何减少浏览器的repaint和reflow?
understanding internet explorer rendering behaviour
notes on html reflow
(2). 慎用with
with(obj){ p = 1}; 代码块的行为实际上是修改了代码块中的执行环境,将obj放在了其作用域链的最前端,在with代码块中访问非局部变量是都是先从obj上开始查找,如果没有再依次按作用域链向上查找,因此使用with相当于增加了作用域链长度。而每次查找作用域链都是要消耗时间的,过长的作用域链会导致查找性能下降。
因此,除非你能肯定在with代码中只访问obj中的属性,否则慎用with,替代的可以使用局部变量缓存需要访问的属性。
(3). 避免使用eval和function
每次 eval 或 function 构造函数作用于字符串表示的源代码时,脚本引擎都需要将源代码转换成可执行代码。这是很消耗资源的操作 —— 通常比简单的函数调用慢100倍以上。
eval 函数效率特别低,由于事先无法知晓传给 eval 的字符串中的内容,eval在其上下文中解释要处理的代码,也就是说编译器无法优化上下文,因此只能有浏览器在运行时解释代码。这对性能影响很大。
function 构造函数比eval略好,因为使用此代码不会影响周围代码;但其速度仍很慢。
此外,使用eval和function也不利于javascript压缩工具执行压缩。
(4). 减少作用域链查找
前文谈到了作用域链查找问题,这一点在循环中是尤其需要注意的问题。如果在循环中需要访问非本作用域下的变量时请在遍历之前用局部变量缓存该变量,并在遍历结束后再重写那个变量,这一点对全局变量尤其重要,因为全局变量处于作用域链的最顶端,访问时的查找次数是最多的。
低效率的写法:
//全局变量
var globalvar = 1;
function mycallback(info){
for( var i = 100000; i–;){
//每次访问globalvar都需要查找到作用域链最顶端,本例中需要访问100000次
globalvar += i;
}
}
更高效的写法:
//全局变量
var globalvar = 1;
function mycallback(info){
//局部变量缓存全局变量
var localvar = globalvar;
for( var i = 100000; i–;){
//访问局部变量是最快的
localvar += i;
}
//本例中只需要访问2次全局变量
globalvar = localvar;
}
此外,要减少作用域链查找还应该减少闭包的使用。
(5). 数据访问
javascript中的数据访问包括直接量(字符串、正则表达式)、变量、对象属性以及数组,其中对直接量和局部变量的访问是最快的,对对象属性以及数组的访问需要更大的开销。当出现以下情况时,建议将数据放入局部变量:
a. 对任何对象属性的访问超过1次
b. 对任何数组成员的访问次数超过1次
另外,还应当尽可能的减少对对象以及数组深度查找。
(6). 字符串拼接
在javascript中使用”+”号来拼接字符串效率是比较低的,因为每次运行都会开辟新的内存并生成新的字符串变量,然后将拼接结果赋值给新变量。与之相比更为高效的做法是使用数组的join方法,即将需要拼接的字符串放在数组中最后调用其join方法得到结果。不过由于使用数组也有一定的开销,因此当需要拼接的字符串较多的时候可以考虑用此方法。
关于javascript优化的更详细介绍请参考:
write efficient javascript(ppt)
efficient javascript
2. css选择符
在大多数人的观念中,都觉得浏览器对css选择符的解析式从左往右进行的,例如
#toc a { color: #444; }
这样一个选择符,如果是从右往左解析则效率会很高,因为第一个id选择基本上就把查找的范围限定了,但实际上浏览器对选择符的解析是从右往左进行的。如上面的选择符,浏览器必须遍历查找每一个a标签的祖先节点,效率并不像之前想象的那样高。根据浏览器的这一行为特点,在写选择符的时候需要注意很多事项,有人已经一一列举了,详情参考此处。
3. html
对html本身的优化现如今也越来越多的受人关注了,详情可以参见这篇总结性文章。
4. image压缩
图片压缩是个技术活,不过现如今这方面的工具也非常多,压缩之后往往能带来不错的效果,具体的压缩原理以及方法在《even faster web sites》第10章有很详细的介绍,有兴趣的可以去看看。
总结
本文从页面级以及代码级两个粒度对前端优化的各种方式做了一个总结,这些方法基本上都是前端开发人员在开发的过程中可以借鉴和实践的,除此之外,完整的前端优化还应该包括很多其他的途径,例如cdn、gzip、多域名、无cookie服务器等等,由于对于开发人员的可操作性并不强大,在此也就不多叙述了,详细的可以参考yahoo和google的这些”金科玉律”。1.javascript简介
html是纯静态的的页面,而javascript让页面有了动态的效果,比如;oa中模块的拖拉
所有的浏览器都会内置javascript的解释器
1992年 nombas公司开发出c减减的嵌入式脚本语言。这是最好的html页面的脚本语言。
netscape为了扩展其浏览器的功能,开发了一套livescript,并与1995年与sun公司联合把其改名为javascript,它的主要目的是处理一些输入的有效性验证,而之前这个工作是留给perl之类的服务器端语言完成,在以前使用电话线调制解调器(28.8kb/s)的时代,如此慢的与服务器交互,这绝对是一件很痛苦的事情。javascript的出现,解决了这个问题,因为它的验证是基于客户端的。
微软公司早期版本的浏览器仅支持自己的vbscript,但后来不得不加入javascript
ie3中搭载javascipt的克隆版本,命名为jscript
在一次技术会议中,sun,microsoft,netscape公司联合制定了ecma-script标准
在2005前,网页上提示框,广告越来越多,把javascipt滥用,使javascript背上了大量的罪名。
2005年,google公司的网上产品(google地图,gmail,google搜索建议)等使得ajax兴起,而javascript便是ajax最重要的元素之一
javascript有三个部分组成
ecmascript dom bom
web标准
网页主要有三部分组成
(结构html,xhtml,表现css,行为dom,ecma)
2.ecma脚本
javascript的语法
区分大小写
弱类型变量 var age=10 var name=”dd”
每行结尾的分号可有可无,但建议还是加上
注释与java相同
变量
变量是通过var关键字来声明的。(variable)
变量的命名规则与java一致
注释有三种:// /**/ <!– –>这个只能注释单行
2.1 javascript的hello world
document.write()是写在文档的最前面
2.2 slice()、substring()、subtr
slice和substring (2,5) 指的是从第3为开始,取(5-2)=3个数,其中slice的参数可以为负
substr(2,5)指的是从第3为开始,取5个数。但ecmascript 没有对该方法进行标准化,因此尽量少使用该方法
2.3 indexof()和lastindexof(),isnan,typeof
indexof(”i”) //从前往后,i在第几位
indexof(”i”,3)可选参数,从第几个字符开始往后找
lastindexof(”i”) //从后往前,i在第几位
lastindexof(“i”,3) //从后往前,i在第几位
如果没找到,则返回-1
string类型的变量,在java中,用””符号表示字符串,用”表示单个字符。而在javascript中这两种都可以
nan(not a number)
alert(nan ==nan)返回false,因此不推荐使用nan本身,推荐函数isnan
alert(isnann(“ab”));//返回false
typeof运算符
var stmp = “test”;
alert(typeof stmp); //输出string
alert(type of 1);//输出number
此外:还有boolean,undefined,object(如果是引用类型或者null值,null值返回object,这其实是ecmascript的一个错误)
当声明的变量未初始化的时候,它的值就是undefined.当没有这个变量的时候,typeof 变
返回的值也是undefined。但是没声明的变量是不能参与计算的。
当函数无明确返回值时,返回的也是undefined
function a(){
}
alert(a() == undefined) //返回true
alert(null == undefined)//返回true
2.4 数值计算
var mynum1 = 23.345;
var mynum2 = 45;
var mynum3 = -34;
var mynum4 = 9e5; //科学计数法为 9*10五次方
var fnumber = 123.456;
alert(fnumber.toexponential(1));//保留的小数点数 1.2e+2
alert(fnumber.toexponential(2));//1.23e+2
.5 布尔值
var married = true;
alert(“1.” + typeof(married));//boolean
married = “true”;
alert(“2.” + typeof(married));//string
.6 类型转换
转换成string类型有三种方式
var a = 3;
var b = a + “”;
var c = a.tostring();
var d = “student” + a;
tostring()
var a=11;
document.write(a.tostring(2) + “<br>”);//转成2进制
document.write(a.tostring(3) + “<br>”);//转成3进制
如果不是数值,则转换报错
parseint()
document.write(parseint(“1red6″) + “<br>”);//返回1,后面非数值的将全部忽略
document.write(parseint(“53.5″) + “<br>”);//返回53
document.write(parseint(“0xc”) + “<br>”); //直接十进制转换12
document.write(parseint(“isaacshun@gmail.com”) + “<br>”);//nan
document.write(parseint(“011″,8) + “<br>”);返回9
document.write(parseint(“011″,10) + “<br>”); //指定为十进制返回11
parsefloat()与parseint()类似
2.7 数组
var amap = new array(“china”,”usa”,”britain”);
amap[20] = “korea”;
alert(amap.length + ” ” + amap[10] + ” ” + amap[20]);//amap[10]返回undefined
document.write(amap.join(“][“) + “<br>”); //用”][“来连接
var sfruit = “apple,pear,peach,orange”;
var afruit = sfruit.split(“,”);
var afruit = [“apple”,”pear”,”peach”,”orange”];
alert(afruit.reverse().tostring());
var afruit = [“pear”,”apple”,”peach”,”orange”];
afruit.sort();
var stack = new array();
stack.push(“red”);
stack.push(“green”);
stack.push(“blue”);
document.write(stack.tostring() + “<br>”);
var vitem = stack.pop(); // blue
document.write(vitem + “<br>”);
document.write(stack.tostring()); // red green
2.8 if语句
//首先获取用户的一个输入,并用number()强制转换为数字
var inumber = number(prompt(“输入一个5到100之间的数字”, “”));//第二个参数,用于显示输入框的默认值
if(isnan(inumber)) //判断输入的是否是数字nan “not a number”
document.write(“请确认你的输入正确”);
else if(inumber > 100 || inumber -1 || suseragent.indexof(“applewebkit”) > -1;
//检测ie、mozilla
var isie = suseragent.indexof(“compatible”) > -1 && suseragent.indexof(“msie”) > -1 && !isopera;
var ismoz = suseragent.indexof(“gecko”) > -1 && !iskhtml;
//检测操作系统
var iswin = (navigator.platform == “win32″) || (navigator.platform == “windows”);
var ismac = (navigator.platform == “mac68k”) || (navigator.platform == “macppc”) || (navigator.platform == “macintosh”);
var isunix = (navigator.platform == “x11″) && !iswin && !ismac;
if(isopera) document.write(“opera “);
if(iskhtml) document.write(“khtml “);
if(isie) document.write(“ie “);
if(ismoz) document.write(“mozilla “);
if(iswin) document.write(“windows”);
if(ismac) document.write(“mac”);
if(isunix) document.write(“unix”);
global对象
其实isnan,paraseint等都是global对象的方法
encodeuri.因为有效的uri不能包含某些字符,如空格。这个方法就是用于将这个字符转换成utf-8编码,使浏览器可以接受他们。
var suil = “www.oseschool.com/pro file/a.html”;
alert(encodeuri(suil));//www.oseschool.com/pro%20file/a.html
即将空格编码成%20
eval方法
eval(“alert(‘hi’)”);
当解析程序发现eval()时,它将把参数解析成真正的ecma-script语句,然后插入该语句所在位置。
global除了有内置方法外,还有很多内置的属性:如:undefined,nan,array,string,number,date,regexp等
math对象
max方法,min方法,ceil,floor,round,sqrt,random
max(1,2,3);min(1.2,3.4);
想取到1~10的数据
math.floor(math.random()*10+1)
2~9的数据
math.floor(math.random()*9+2);
3.1 getelementsbytagname
function searchdom(){
//放在函数内,页面加载完成后才用<body>的onload加载,这时如果把alert这句改成用document.write则会把原内容覆盖掉,因为是后面才执行的
var oli = document.getelementsbytagname(“li”);
//输出长度、标签名称以及某项的文本节点值
alert(oli.length + ” ” +oli[0].tagname + ” ” + oli[3].childnodes[0].nodevalue);
var oul = document.getelementsbytagname(“ul”);
var oli2 = oul[1].getelementsbytagname(“li”);
alert(oli2.length + ” ” +oli2[0].tagname + ” ” + oli2[1].childnodes[0].nodevalue);
}
<body onload=”searchdom()”>
<ul>客户端语言
<li>html</li>
<li>javascript</li>
<li id=”cssli”>css</li>
</ul>
<ul>服务器端语言
<li>asp.net</li>
<li>jsp</li>
<li>php</li>
</ul>
</body>
3.2 getelementbyid
var oli = document.getelementbyid(“cssli”);
oli.style .backgroundcolor=”yellow”
//输出标签名称以及文本节点值
alert(oli.tagname + ” ” + oli.childnodes[0].nodevalue);
3.2 getelementsbyname
alert(document.getelementsbyname(“a”).length);
3.3 访问子节点
function mydominspector(){
var oul = document.getelementbyid(“mylist”); //获取<ul>标记
var domstring = “”;
if(oul.haschildnodes()){ //判断是否有子节点
var och = oul.childnodes;
for(var i=0;i<och.length;i++) //逐一查找
domstring += och[i].nodename + “\n”;
}
alert(domstring);
}
4访问父节点
nodename如果为文本节点,则返回#text
tagname如果为文本节点,则返回undefined
function mydominspector(){
var myitem = document.getelementbyid(“mydearfood”);
alert(myitem.parentnode.tagname);
}
function mydominspector(){
var myitem = document.getelementbyid(“mydearfood”);
var parentelm = myitem.parentnode;
while(parentelm.classname != “colorful” && parentelm != document.body)
parentelm = parentelm.parentnode; //一路往上找
alert(parentelm.tagname);
}
<body onload=”mydominspector()”>
<p class=”colorful”>
<ul>
<li>糖醋排骨</li>
<li>圆笼粉蒸肉</li>
<li>泡菜鱼</li>
<li id=”mydearfood”>板栗烧鸡</li>
<li>麻婆豆腐</li>
</ul>
</p>
</body>
访问兄弟节点
function mydominspector(){
var myitem = document.getelementbyid(“mydearfood”);
//访问兄弟节点
var nextlistitem = myitem.nextsibling;
var prelistitem = myitem.previoussibling;
alert(nextlistitem.tagname +” “+ prelistitem.tagname);
}
在firefox中不支持,但是ie中却是支持的。
3.6 第一个最后一个子节点
function nextsib(node){
var templast = node.parentnode.lastchild;
//判断是否是最后一个节点,如果是则返回null
if(node == templast)
return null;
var tempobj = node.nextsibling;
//逐一搜索后面的兄弟节点,直到发现元素节点为止
while(tempobj.nodetype!=1 && tempobj.nextsibling!=null)
tempobj = tempobj.nextsibling;
//三目运算符,如果是元素节点则返回节点本身,否则返回null
return (tempobj.nodetype==1)?tempobj:null;
}
function prevsib(node){
var tempfirst = node.parentnode.firstchild;
//判断是否是第一个节点,如果是则返回null
if(node == tempfirst)
return null;
var tempobj = node.previoussibling;
//逐一搜索前面的兄弟节点,直到发现元素节点为止
while(tempobj.nodetype!=1 && tempobj.previoussibling!=null)
tempobj = tempobj.previoussibling;
return (tempobj.nodetype==1)?tempobj:null;
}
function mydominspector(){
var myitem = document.getelementbyid(“mydearfood”);
//获取后一个元素兄弟节点
var nextlistitem = nextsib(myitem);
//获取前一个元素兄弟节点
var prelistitem = prevsib(myitem);
alert(“后一项:” + ((nextlistitem!=null)?nextlistitem.firstchild.nodevalue:null) + ” 前一项:” + ((prelistitem!=null)?prelistitem.firstchild.nodevalue:null) );
}
nodetype
元素element
1
属性attr
2
文本text
3
注释comments
8
文档document
9
function showattr(){
var btnshowattr=document.getelementbyid(“btnshowattr”); //演示按钮,有很多属性
var attrs=btnshowattr.attributes;
for(var i=0;i<attrs.length ;i++){
var attr=attrs[i];
alert(‘nodetype:’+attr.nodetype); //attribute 的nodetype=2
alert(‘attr:’+attr);
alert(‘attr.name:’+attr.name+’=’+attr.value);
}
function showdocument(){
alert(‘nodetype:’+document.nodetype); //9
alert(‘nodename:’+document.nodename);
alert(document);
}
3.7 getattribute setattribute
function mydominspector(){
//获取图片
var myimg = document.getelementsbytagname(“img”)[0];
//获取图片title属性
alert(myimg.getattribute(“title”));
}
function changepic(){
//获取图片
var myimg = document.getelementsbytagname(“img”)[0];
//设置图片src和title属性
myimg.setattribute(“src”,”02.jpg”);
myimg.setattribute(“title”,”紫荆公寓”);
}
3.8创建新节点
function createp(){
var op = document.createelement(“p”);
var otext = document.createtextnode(“hhhhh”);
op.appendchild(otext);
op.setattribute(“style”,”text-align:center”);
document.body.appendchild(op);
//创建完节点,就马上会影响到下面的操作,比如p的数量就会多1个
}
3.9删除节点
需要注意的是标签之间的父子关系!!!
function deletep(){
var op = document.getelementsbytagname(“p”)[0];
op.parentnode.removechild(op); //删除节点
}
3.10替换节点
function replacep(){
var ooldp = document.getelementsbytagname(“p”)[0];
var onewp = document.createelement(“p”); //新建节点
var otext = document.createtextnode(“这是一个感人肺腑的故事”);
onewp.appendchild(otext);
ooldp.parentnode.replacechild(onewp,ooldp); //替换节点
}
3.11插入节点
function insertp(){
var ooldp = document.getelementsbytagname(“p”)[0];
var onewp = document.createelement(“p”); //新建节点
var otext = document.createtextnode(“这是一个感人肺腑的故事”);
onewp.appendchild(otext);
ooldp.parentnode.insertbefore(onewp,ooldp); //插入节点
}
没有insertafter,但是可以自己写一个
function insertafter(newelement, targetelement){
var oparent = targetelement.parentnode; //首先找到目标元素的父元素
if(oparent.lastchild == targetelement) //如果目标元素已经是最后一个子元素了
oparent.appendchild(newelement); //则直接用appendchild()加到子元素列表的最后
else //否则用insertbefore()插入到目标元素的下一个兄弟元素之前
oparent.insertbefore(newelement,targetelement.nextsibling);
}
function insertp(){
var ooldp = document.getelementbyid(“mytarget”);
var onewp = document.createelement(“p”); //新建节点
var otext = document.createtextnode(“这是一个感人肺腑的故事”);
onewp.appendchild(otext);
insertafter(onewp,ooldp); //插入节点
}
其实这个也是通过insertbefore原理来实现的
3.12 clonenode(deepboolean)
复制并返回当前节点的复制节点,这个复制得到的节点是一个孤立的节点,不在document树中。复制原来节点的属性值,包括id属性,所以在把这个新节点加到document之前,一定要修改id属性,以便使它保持唯一。当然如果id的唯一性不重要可以不做处理。
这个方法支持一个布尔参数,当deepboolean设置true时,复制当前节点的所有子节点,包括该节点内的文本。
<p id=”mypara”>11111</p>
p=document.getelementbyid(“mypara”)
pclone = p.clonenode(true);
p.parentnode.appendchild(pclone
3.12文档碎片
function insertps(){
var acolors = [“red”,”green”,”blue”,”magenta”,”yellow”,”chocolate”,”black”,”aquamarine”,”lime”,”fuchsia”,”brass”,”azure”,”brown”,”bronze”,”deeppink”,”aliceblue”,”gray”,”copper”,”coral”,”feldspar”,”orange”,”orchid”,”pink”,”plum”,”quartz”,”purple”];
var ofragment = document.createdocumentfragment(); //创建文档碎片
for(var i=0;i<acolors.length;i++){
var op = document.createelement(“p”);
var otext = document.createtextnode(acolors[i]);
op.appendchild(otext);
ofragment.appendchild(op); //将节点先添加到碎片中
}
document.body.appendchild(ofragment); //最后一次性添加到页面
}
3.13 innerhtml
function mydominnerhtml(){
var myp = document.getelementbyid(“mytest”);
alert(myp.innerhtml); //直接显示innerhtml的内容
//修改innerhtml,可直接添加代码
myp.innerhtml = “<img src=’01.jpg’ title=’情人坡’>”;
}
innerhtml可同时显示没有的代码
3.14 换皮肤
<style type=”text/css”>
.myul1{
color:#0000ff;
font-family:arial;
font-weight:bold;
}
.myul2{
color:#ff0000;
font-family:georgia, “times new roman”, times, serif;
}
</style>
<script language=”javascript”>
function check(){
var omy = document.getelementsbytagname(“ul”)[0];
omy.classname =(omy.classname==”myul1″? “myul2″:”myul1″); //修改css类
}
</script>
</head>
<body>
<ul onclick=”check()” class=”myul1″>
<li>html</li>
<li>javascript</li>
<li>css</li>
</ul>
</body>
4.表格与表单
4.1 动态添加行
<script language=”javascript”>
window.onload=function(){
var otr = document.getelementbyid(“member”).insertrow(2); //插入一行
var atext = new array();
atext[0] = document.createtextnode(“fresheggs”);
atext[1] = document.createtextnode(“w610″);
atext[2] = document.createtextnode(“nov 5th”);
atext[3] = document.createtextnode(“scorpio”);
atext[4] = document.createtextnode(“1038818″);
for(var i=0;i
otd.appendchild(atext[i]);
}
}
</script>
4.2修改单元格内容
<script language=”javascript”>
window.onload=function(){
var otable = document.getelementbyid(“member”);
otable.rows[3].cells[4].innerhtml = “lost”; //修改单元格内容
}
</script>
4.3 动态删除
parentelement是ie dom,
parentnode是标准dom
<script language=”javascript”>
window.onload=function(){
var otable = document.getelementbyid(“member”);
otable.deleterow(2); //删除一行,后面的行号自动补齐//指从table中的第2行开始进行删除
otable.rows[2].deletecell(1); //删除一个单元格,后面的也自动补齐
}
</script>
<script language=”javascript”>
function mydelete(){
var otable = document.getelementbyid(“member”);
//删除该行
this.parentnode.parentnode.parentnode.removechild(this.parentnode.parentnode);
}
window.onload=function(){
var otable = document.getelementbyid(“member”);
var otd;
//动态添加delete链接
for(var i=1;i<otable.rows.length;i++){
otd = otable.rows[i].insertcell(5);
otd.innerhtml = “<a href=’#’>delete</a>”;
otd.firstchild.onclick = mydelete; //添加删除事件
}
}
</script>
4.4动态删除列
<script language=”javascript”>
function deletecolumn(otable,inum){
//自定义删除列函数,即每行删除相应单元格
for(var i=0;i class >tag ,另外在同级样式按照申明的顺序后出现的样式具有高优先级。
5. 前端页面由哪三层构成,分别是什么?作用是什么?
网页分成三个层次,即:结构层、表示层、行为层。
网页的结构层(structurallayer)由html 或xhtml 之类的标记语言负责创建。标签,也就是那些出现在尖括号里的单词,对网页内容的语义含义做出了描述,但这些标签不包含任何关于如何显示有关内容的信息。例如,p标签表达了这样一种语义:”这是一个文本段。”
网页的表示层(presentationlayer)由css 负责创建。css 对”如何显示有关内容”的问题做出了回答。
网页的行为层(behaviorlayer)负责回答”内容应该如何对事件做出反应”这一问题。这是javascript 语言和dom 主宰的领域。
6. css的基本语句构成是?
选择器{属性1:值1;属性2:值2;……}
7. 你做的页面在哪些流览器测试过?这些浏览器的内核分别是什么?经常遇到的浏览器的兼容性有哪些?怎么会出现?解决方法是什么?
ie内核浏览器:360,傲游,搜狗,世界之窗,腾讯tt
非ie内核浏览器:firefoxopera safari chrome
8. 如何居中一个浮动元素?
设置容器的浮动方式为相对定位,然后确定容器的宽高,比如宽500 高 300 的层,然后设置层的外边距。
p{width:500px;height:300px;margin: -150px 0 0-250px;position:relative;left:50%;top:50%;}
9. 有没有关注html5和css3?如有请简单说一些您对它们的了解情况!
html5标签的改变:<header>,<footer>, <dialog>, <aside>, <figure>, <section>等
ie9以上开始支持
css3实现圆角,阴影,对文字加特效,增加了更多的css选择器。
10. 如果让你来制作一个访问量很高的大型网站,你会如何来管理所有css文件、js与图片?
11. 你对前端界面工程师这个职位是怎么样理解的?它的前景会怎么样?
以下为web前端开发笔试题集锦之javascript篇,移步html/css篇
1, 判断字符串是否是这样组成的,第一个必须是字母,后面可以是字母、数字、下划线,总长度为5-20
var reg = /^[a-za-z][a-za-z_0-9]{4,19}$/;
reg.test(“a1a__a1a__a1a__a1a__”);
2,截取字符串abcdefg的efg
var str = “abcdefg”;
if (/efg/.test(str)) {
var efg = str.substr(str.indexof(“efg”), 3);
alert(efg);
}
3,判断一个字符串中出现次数最多的字符,统计这个次数
//将字符串的字符保存在一个hash table中,key是字符,value是这个字符出现的次数
var str = “abcdefgaddda”;
var obj = {};
for (var i = 0, l = str.length; i < l; i++) {
var key = str[i];
if (!obj[key]) {
obj[key] = 1;
} else {
obj[key]++;
}
}
/*遍历这个hash table,获取value最大的key和value*/
var max = -1;
var max_key = “”;
var key;
for (key in obj) {
if (max < obj[key]) {
max = obj[key];
max_key = key;
}
}
alert(“max:”+max+” max_key:”+max_key);
4,ie与ff脚本兼容性问题
(1) window.event:
表示当前的事件对象,ie有这个对象,ff没有,ff通过给事件处理函数传递事件对象
(2) 获取事件源
ie用srcelement获取事件源,而ff用target获取事件源
(3) 添加,去除事件
ie:element.attachevent(“onclick”, function) element.detachevent(“onclick”, function)
ff:element.addeventlistener(“click”, function, true) element.removeeventlistener(“click”, function, true)
(4) 获取标签的自定义属性
ie:p1.value或p1[“value”]
ff:可用p1.getattribute(“value”)
(5) document.getelementbyname()和document.all[name]
ie;document.getelementbyname()和document.all[name]均不能获取p元素
ff:可以
(6) input.type的属性
ie:input.type只读
ff:input.type可读写
(7) innertext textcontent outerhtml
ie:支持innertext, outerhtml
ff:支持textcontent
(8) 是否可用id代替html元素
ie:可以用id来代替html元素
ff:不可以
这里只列出了常见的,还有不少,更多的介绍可以参看javascript在ie浏览器和firefox浏览器中的差异总结
5,规避javascript多人开发函数重名问题
(1) 可以开发前规定命名规范,根据不同开发人员开发的功能在函数前加前缀
(2) 将每个开发人员的函数封装到类中,调用的时候就调用类的函数,即使函数重名只要类名不重复就ok
6,javascript面向对象中继承实现
javascript面向对象中的继承实现一般都使用到了构造函数和prototype原型链,简单的代码如下:
function animal(name) {
this.name = name;
}
animal.prototype.getname = function() {alert(this.name)}
function dog() {};
dog.prototype = new animal(“buddy”);
dog.prototype.constructor = dog;
var dog = new dog();
7,ff下面实现outerhtml
ff不支持outerhtml,要实现outerhtml还需要特殊处理
思路如下:
在页面中添加一个新的元素a,克隆一份需要获取outerhtml的元素,将这个元素append到新的a中,然后获取a的innerhtml就可以了。
<!doctype html public “-//w3c//dtd xhtml 1.0 transitional//en” “http://www.w3.org/tr/xhtml1/dtd/xhtml1-transitional.dtd”>
<html xmlns=”http://www.w3.org/1999/xhtml”>
<head>
<meta http-equiv=”content-type” content=”text/html; charset=gb2312″ />
<title>获取outerhmtl</title>
<style>
p{ background:#0000ff;width:100px;height:100px;}
span{ background:#00ff00;width:100px;height:100px;}
p{ background:#ff0000;width:100px;height:100px;}
</style>
</head>
<body>
<p id=”a”><span>span</span>p</p>
<span>span</span>
<p>p</p>
<script type=”text/javascript”>
function getouterhtml(id){
var el = document.getelementbyid(id);
var newnode = document.createelement(“p”);
document.appendchild(newnode);
var clone = el.clonenode(true);
newnode.appendchild(clone);
alert(newnode.innerhtml);
document.removechild(newnode);
}
getouterhtml(“a”);
</script>
</body>
</html>
8,编写一个方法求一个字符串的字节长度
假设:
一个英文字符占用一个字节,一个中文字符占用两个字节
function getbytes(str){
var len = str.length;
var bytes = len;
for(var i=0; i<len; i++){
if (str.charcodeat(i) > 255) bytes++;
}
return bytes;
}
alert(getbytes(“你好,as”));
9,编写一个方法去掉一个数组的重复元素
var arr = [1 ,1 ,2, 3, 3, 2, 1];
array.prototype.unique = function(){
var ret = [];
var o = {};
var len = this.length;
for (var i=0; i<len; i++){
var v = this[i];
if (!o[v]){
o[v] = 1;
ret.push(v);
}
}
return ret;
};
alert(arr.unique());
10,写出3个使用this的典型应用
(1)在html元素事件属性中使用,如
<input type=”button” onclick=”showinfo(this);” value=”点击一下”/>
(2)构造函数
function animal(name, color) {
this.name = name;
this.color = color;
}
(3)
<input type=”button” id=”text” value=”点击一下” />
<script type=”text/javascript”>
var btn = document.getelementbyid(“text”);
btn.onclick = function() {
alert(this.value); //此处的this是按钮元素
}
</script>
(4)css expression表达式中使用this关键字
<table width=”100px” height=”100px”>
<tr>
<td>
<p style=”width:expression(this.parentnode.width);”>p element</p>
</td>
</tr>
</table>
12,如何显示/隐藏一个dom元素?
el.style.display = “”;
el.style.display = “none”;
el是要操作的dom元素
13,javascript中如何检测一个变量是一个string类型?请写出函数实现
string类型有两种生成方式:
(1)var str = “hello world”;
(2)var str2 = new string(“hello world”);
function isstring(str){
return (typeof str == “string” || str.constructor == string);
}
var str = “”;
alert(isstring(1));
alert(isstring(str));
alert(isstring(new string(str)));
14,网页中实现一个计算当年还剩多少时间的倒数计时程序,要求网页上实时动态显示”××年还剩××天××时××分××秒”
<!doctype html public “-//w3c//dtd html 4.01 transitional//en” “http://www.w3.org/tr/html4/loose.dtd”>
<html>
<head>
<meta http-equiv=”content-type” content=”text/html; charset=utf-8″>
<title>倒计时</title>
</head>
<body>
<input type=”text” value=”” id=”input” size=”1000″/>
<script type=”text/javascript”>
function counter() {
var date = new date();
var year = date.getfullyear();
var date2 = new date(year, 12, 31, 23, 59, 59);
var time = (date2 – date)/1000;
var day =math.floor(time/(24*60*60))
var hour = math.floor(time%(24*60*60)/(60*60))
var minute = math.floor(time%(24*60*60)%(60*60)/60);
var second = math.floor(time%(24*60*60)%(60*60)%60);
var str = year + “年还剩”+day+”天”+hour+”时”+minute+”分”+second+”秒”;
document.getelementbyid(“input”).value = str;
}
window.setinterval(“counter()”, 1000);
</script>
</body>
</html>
15,补充代码,鼠标单击button1后将button1移动到button2的后面<p><input type=”button” id =”button1″ value=”1″ onclick=”?”><input type=”button” id =”button2″ value=”2″ /”></p>
<p>
<input type=”button” id =”button1″ value=”1″ onclick=”movebtn(this);”>
<input type=”button” id =”button2″ value=”2″ />
</p>
<script type=”text/javascript”>
function movebtn(obj) {
var clone = obj.clonenode(true);
var parent = obj.parentnode;
parent.appendchild(clone);
parent.removechild(obj);
}
</script>
16,javascript有哪几种数据类型
简单:number,boolean,string,null,undefined
复合:object,array,function
17,下面css标签在javascript中调用应如何拼写,border-left-color,-moz-viewport
borderleftcolor
mozviewport
18,javascript中如何对一个对象进行深度clone
function cloneobject(o) {
if(!o || ‘object’ !== typeof o) {
return o;
}
var c = ‘function’ === typeof o.pop ? [] : {};
var p, v;
for(p in o) {
if(o.hasownproperty(p)) {
v = o[p];
if(v && ‘object’ === typeof v) {
c[p] = ext.ux.clone(v);
}
else {
c[p] = v;
}
}
}
return c;
};
19,如何控制alert中的换行
\n alert(“p\np”);
20,请实现,鼠标点击页面中的任意标签,alert该标签的名称.(注意兼容性)
<!doctype html public “-//w3c//dtd xhtml 1.0 transitional//en” “http://www.w3.org/tr/xhtml1/dtd/xhtml1-transitional.dtd”>
<html xmlns=”http://www.w3.org/1999/xhtml”>
<head>
<meta http-equiv=”content-type” content=”text/html; charset=gb2312″ />
<title>鼠标点击页面中的任意标签,alert该标签的名称</title>
<style>
p{ background:#0000ff;width:100px;height:100px;}
span{ background:#00ff00;width:100px;height:100px;}
p{ background:#ff0000;width:100px;height:100px;}
</style>
<script type=”text/javascript”>
document.onclick = function(evt){
var e = window.event || evt;
var tag = e[“target”] || e[“srcelement”];
alert(tag.tagname);
};
</script>
</head>
<body>
<p id=”p”><span>span</span>p</p>
<span>span</span>
<p>p</p>
</body>
</html>
21,请编写一个javascript函数 parsequerystring,它的用途是把url参数解析为一个对象,如:
var url = “http://witmax.cn/index.php?key0=0&key1=1&key2=2″;
function parsequerystring(url){
var params = {};
var arr = url.split(“?”);
if (arr.length <= 1)
return params;
arr = arr[1].split(“&”);
for(var i=0, l=arr.length; i<l; i++){
var a = arr[i].split(“=”);
params[a[0]] = a[1];
}
return params;
}
var url = “http://witmax.cn/index.php?key0=0&key1=1&key2=2″;
var ps = parsequerystring(url);
alert(ps[“key1”]);
22,ajax是什么? ajax的交互模型? 同步和异步的区别? 如何解决跨域问题?
ajax是多种技术组合起来的一种浏览器和服务器交互技术,基本思想是允许一个互联网浏览器向一个远程页面/服务做异步的http调用,并且用收到的数据来更新一个当前web页面而不必刷新整个页面。该技术能够改进客户端的体验。包含的技术:
xhtml:对应w3c的xhtml规范,目前是xhtml1.0。
css:对应w3c的css规范,目前是css2.0
dom:这里的dom主要是指html dom,xml dom包括在下面的xml中
javascript:对应于ecma的ecmascript规范
xml:对应w3c的xml dom、xslt、xpath等等规范
xmlhttprequest:对应whatwg的web applications1.0规范(http://whatwg.org/specs/web-apps/current-work/)
ajax交互模型
同步:脚本会停留并等待服务器发送回复然后再继续
异步:脚本允许页面继续其进程并处理可能的回复
跨域问题简单的理解就是因为js同源策略的限制,a.com域名下的js无法操作b.com或c.a.com下的对象,具体场景如下:
ps:(1)如果是端口或者协议造成的跨域问题前端是无能为力的
(2) 在跨域问题上,域仅仅通过url的首部来识别而不会尝试判断相同的ip地址对应的域或者两个域是否对应一个ip
前端对于跨域的解决办法:
(1) document.domain+iframe
(2) 动态创建script标签
23,什么是闭包?下面这个ul,如何点击每一列的时候alert其index?
<ul id=”test”>
<li>这是第一条</li>
<li>这是第二条</li>
<li>这是第三条</li>
</ul>
内部函数被定义它的函数的外部区域调用的时候就产生了闭包。
(function a() {
var index = 0;
var ul = document.getelementbyid(“test”);
var obj = {};
for (var i = 0, l = ul.childnodes.length; i < l; i++) {
if (ul.childnodes[i].nodename.tolowercase() == “li”) {
var li = ul.childnodes[i];
li.onclick = function() {
index++;
alert(index);
}
}
}
})();
24,请给出异步加载js方案,不少于两种
默认情况javascript是同步加载的,也就是javascript的加载时阻塞的,后面的元素要等待javascript加载完毕后才能进行再加载,对于一些意义不是很大的javascript,如果放在页头会导致加载很慢的话,是会严重影响用户体验的。
异步加载方式:
(1) defer,只支持ie
(2) async:
(3) 创建script,插入到dom中,加载完毕后callback,见代码:
function loadscript(url, callback){
var script = document.createelement(“script”)
script.type = “text/javascript”;
if (script.readystate){ //ie
script.onreadystatechange = function(){
if (script.readystate == “loaded” ||
script.readystate == “complete”){
script.onreadystatechange = null;
callback();
}
};
} else { //others: firefox, safari, chrome, and opera
script.onload = function(){
callback();
};
}
script.src = url;
document.body.appendchild(script);
}
25,请设计一套方案,用于确保页面中js加载完全。
var n = document.createelement(“script”);
n.type = “text/javascript”;
//以上省略部分代码
//ie支持script的readystatechange属性(ie support the readystatechange event for script and css nodes)
if(ua.ie){
n.onreadystatechange = function(){
var rs = this.readystate;
if(‘loaded’ === rs || ‘complete’===rs){
n.onreadystatechange = null;
f(id,url); //回调函数
}
};
//省略部分代码
//safari 3.x supports the load event for script nodes(dom2)
n.addeventlistener(‘load’,function(){
f(id,url);
});
//firefox and opera support onload(but not dom2 in ff) handlers for
//script nodes.opera, but no ff, support the onload event for link
//nodes.
}else{
n.onload = function(){
f(id,url);
};
}
26,js中如何定义class,如何扩展prototype?
ele.classname = “***”; //***在css中定义,形式如下:.*** {…}
a.prototype.b = c;
a是某个构造函数的名字
b是这个构造函数的属性
c是想要定义的属性的值
27,如何添加html元素的事件,有几种方法.
(1)为html元素的事件属性赋值
(2)在js中使用ele.on*** = function() {…}
(3)使用dom2的添加事件的方法 addeventlistener或attachevent
28,documen.write和 innerhtml的区别
document.write只能重绘整个页面
innerhtml可以重绘页面的一部分
29,多浏览器检测通过什么?
(1) navigator.useragent
(2)不同浏览器的特性,如addeventlistener
30,js的基础对象有那些, window和document的常用的方法和属性列出来
string,number,boolean
window:
方法:setinterval,settimeout,clearinterval,cleartimeout,alert,confirm,open
属性:name,parent,screenleft,screentop,self,top,status
document
方法:createelement,execcommand,getelementbyid,getelementsbyname,getelementbytagname,write,writeln
属性:cookie,doctype,domain,documentelement,readystate,url,
31,前端开发的优化问题
(1)减少http请求次数:css spirit,data uri
(2) js,css源码压缩
(3)前端模板 js+数据,减少由于html标签导致的带宽浪费,前端用变量保存ajax请求结果,每次操作本地变量,不用请求,减少请求次数
(4)用innerhtml代替dom操作,减少dom操作次数,优化javascript性能
(5)用settimeout来避免页面失去响应
(6)用hash-table来优化查找
(7)当需要设置的样式很多时设置classname而不是直接操作style
(8)少用全局变量
(9)缓存dom节点查找的结果
(10)避免使用css expression
(11)图片预载
(12)避免在页面的主体布局中使用table,table要等其中的内容完全下载之后才会显示出来,显示比p+css布局慢
32,如何控制网页在网络传输过程中的数据量
启用gzip压缩
保持良好的编程习惯,避免重复的css,javascript代码,多余的html标签和属性
33,flash、ajax各自的优缺点,在使用中如何取舍?
ajax的优势
(1)可搜索型
(2)开放性
(3)费用
(4)易用性
(5)易于开发
flash的优势
(1)多媒体处理
(2)兼容性
(3)矢量图形比svg,canvas优势大很多
(4)客户端资源调度,比如麦克风,摄像头
4用脚本写去除字符串的前后空格
string.prototype.trim = function(mode)
{//前后去空格
if (mode==’left’) {
return ((this.charat(0) == ” ” && this.length > 0) ? this.slice(1).trim(‘left’) : this);
} else
if (mode == ‘right’) {
return ((this.charat(this.length – 1) == ” ” && this.length > 0) ? this.slice(0, this.length – 1).trim(‘right’) : this);
} else {
return this.trim(‘left’).trim(‘right’);
}
};
5算出字符串中出现次数最多的字符是什么,出现了多少次
<script type=”text/javascript”>
//<![cdata[var str =”adadfdfseffserfefsefseeffffftsdg”; //命名一个变量放置给出的字符串
var maxlength = 0; //命名一个变量放置字母出现的最高次数并初始化为0
var result = ”; //命名一个变量放置结果输入
while( str != ” ){ //循环迭代开始,并判断字符串是否为空
oldstr = str; //将原始的字符串变量赋值给新变量
getstr = str.substr(0,1); //用字符串的substr的方法得到第一个字符(首字母)
eval(“str = str.replace(/”+getstr+”/g,”)”); //详细如补充
if( oldstr.length-str.length > maxlength )
{ //判断原始的字符串的长度减去替代后字符串长度是否大于之前出现的最大的字符串长度
maxlength = oldstr.length-str.length; //两字符串长度相减得到最大的字符串长度
result = getstr + “=” + maxlength //返回最大的字符串结果(字母、出现次数)
}
}
alert(result) //弹出结果
//]]>
</script>
6写出3中使用this的典型应用
在html元素事件属性中inline方式使用this关键字:
7.最后一个问题是,如何制作一个combo选项,就是可以输入可以下拉菜单选择。
这个网上有很多成品,主要是通过绝对定位和相对定位结合的方式,改造原有的select 标签和input标签的方法。
附上javascript正则表达式的基本知识:
g: 全局匹配
i: 忽略大小写
^ 匹配一个输入或一行的开头,/^a/匹配”an a”,而不匹配”an a”
/匹配”an a”,而不匹配”an a”
* 匹配前面元字符0次或多次,/ba*/将匹配b,ba,baa,baaa
+ 匹配前面元字符1次或多次,/ba*/将匹配ba,baa,baaa
? 匹配前面元字符0次或1次,/ba*/将匹配b,ba
(x) 匹配x保存x在名为
9的变量中
x|y 匹配x或y
{n} 精确匹配n次
{n,} 匹配n次以上
{n,m} 匹配n-m次
[xyz] 字符集(character set),匹配这个集合中的任一一个字符(或元字符)
[^xyz] 不匹配这个集合中的任何一个字符
[b] 匹配一个退格符
b 匹配一个单词的边界
b 匹配一个单词的非边界
cx 这儿,x是一个控制符,/cm/匹配ctrl-m
d 匹配一个字数字符,/d/ = /[0-9]/
d 匹配一个非字数字符,/d/ = /[^0-9]/
n 匹配一个换行符
r 匹配一个回车符
s 匹配一个空白字符,包括n,r,f,t,v等
s 匹配一个非空白字符,等于/[^nfrtv]/
t 匹配一个制表符
v 匹配一个重直制表符
w 匹配一个可以组成单词的字符(alphanumeric,这是我的意译,含数字),包括下划线,如[w]匹配”$5.98″中的5,等于[a-za-z0-9]
w 匹配一个不可以组成单词的字符,如[w]匹配”
,等于[^a-za-z0-9
举例:验证email
var myreg = /^[_a-z0-9]+@([_a-z0-9]+.)+[a-z0-9]{2,3}$/;
if(myreg.test(stremail)) return true;
date 对象属性
ff: firefox, ie: internet explorer
属性描述 ff ie
constructor 返回对创建此对象的 date 函数的引用。 1 4
prototype 使您有能力向对象添加属性和方法。 1 4
date 对象方法
ff: firefox, ie: internet explorer
方法描述 ff ie
date() 返回当日的日期和时间。 1 3
getdate() 从 date 对象返回一个月中的某一天 (1 ~ 31)。 1 3
getday() 从 date 对象返回一周中的某一天 (0 ~ 6)。 1 3
getmonth() 从 date 对象返回月份 (0 ~ 11)。 1 3
getfullyear() 从 date 对象以四位数字返回年份。 1 4
getyear() 请使用 getfullyear() 方法代替。 1 3
gethours() 返回 date 对象的小时 (0 ~ 23)。 1 3
getminutes() 返回 date 对象的分钟 (0 ~ 59)。 1 3
getseconds() 返回 date 对象的秒数 (0 ~ 59)。 1 3
getmilliseconds() 返回 date 对象的毫秒(0 ~ 999)。 1 4
gettime() 返回 1970 年 1 月 1 日至今的毫秒数。 1 3
gettimezoneoffset() 返回本地时间与格林威治标准时间 (gmt) 的分钟差。 1 3
getutcdate() 根据世界时从 date 对象返回月中的一天 (1 ~ 31)。 1 4
getutcday() 根据世界时从 date 对象返回周中的一天 (0 ~ 6)。 1 4
getutcmonth() 根据世界时从 date 对象返回月份 (0 ~ 11)。 1 4
getutcfullyear() 根据世界时从 date 对象返回四位数的年份。 1 4
getutchours() 根据世界时返回 date 对象的小时 (0 ~ 23)。 1 4
getutcminutes() 根据世界时返回 date 对象的分钟 (0 ~ 59)。 1 4
getutcseconds() 根据世界时返回 date 对象的秒钟 (0 ~ 59)。 1 4
getutcmilliseconds() 根据世界时返回 date 对象的毫秒(0 ~ 999)。 1 4
parse() 返回1970年1月1日午夜到指定日期(字符串)的毫秒数。 1 3
setdate() 设置 date 对象中月的某一天 (1 ~ 31)。 1 3
setmonth() 设置 date 对象中月份 (0 ~ 11)。 1 3
setfullyear() 设置 date 对象中的年份(四位数字)。 1 4
setyear() 请使用 setfullyear() 方法代替。 1 3
sethours() 设置 date 对象中的小时 (0 ~ 23)。 1 3
setminutes() 设置 date 对象中的分钟 (0 ~ 59)。 1 3
setseconds() 设置 date 对象中的秒钟 (0 ~ 59)。 1 3
setmilliseconds() 设置 date 对象中的毫秒 (0 ~ 999)。 1 4
settime() 以毫秒设置 date 对象。 1 3
setutcdate() 根据世界时设置 date 对象中月份的一天 (1 ~ 31)。 1 4
setutcmonth() 根据世界时设置 date 对象中的月份 (0 ~ 11)。 1 4
setutcfullyear() 根据世界时设置 date 对象中的年份(四位数字)。 1 4
setutchours() 根据世界时设置 date 对象中的小时 (0 ~ 23)。 1 4
setutcminutes() 根据世界时设置 date 对象中的分钟 (0 ~ 59)。 1 4
setutcseconds() 根据世界时设置 date 对象中的秒钟 (0 ~ 59)。 1 4
setutcmilliseconds() 根据世界时设置 date 对象中的毫秒 (0 ~ 999)。 1 4
tosource() 返回该对象的源代码。 1 –
tostring() 把 date 对象转换为字符串。 1 4
totimestring() 把 date 对象的时间部分转换为字符串。 1 4
todatestring() 把 date 对象的日期部分转换为字符串。 1 4
togmtstring() 请使用 toutcstring() 方法代替。 1 3
toutcstring() 根据世界时,把 date 对象转换为字符串。 1 4
tolocalestring() 根据本地时间格式,把 date 对象转换为字符串。 1 3
tolocaletimestring() 根据本地时间格式,把 date 对象的时间部分转换为字符串。 1 3
tolocaledatestring() 根据本地时间格式,把 date 对象的日期部分转换为字符串。 1 3
utc() 根据世界时返回 1997 年 1 月 1 日到指定日期的毫秒数。 1 3
valueof() 返回 date 对象的原始值。 1 4
array 对象属性
ff: firefox, ie: internet explorer
属性
描述
ff
ie
constructor
返回对创建此对象的数组函数的引用。
1
4
index
1
4
input
1
4
length
设置或返回数组中元素的数目。
1
4
prototype
使您有能力向对象添加属性和方法。
1
4
array 对象方法
ff: firefox, ie: internet explorer
方法
描述
ff
ie
concat()
连接两个或更多的数组,并返回结果。
1
4
join()
把数组的所有元素放入一个字符串。元素通过指定的分隔符进行分隔。
1
4
pop()
删除并返回数组的最后一个元素
1
5.5
push()
向数组的末尾添加一个或更多元素,并返回新的长度。
1
5.5
reverse()
颠倒数组中元素的顺序。
1
4
shift()
删除并返回数组的第一个元素
1
5.5
slice()
从某个已有的数组返回选定的元素
1
4
sort()
对数组的元素进行排序
1
4
splice()
删除元素,并向数组添加新元素。
1
5.5
tosource()
返回该对象的源代码。
1
-
tostring()
把数组转换为字符串,并返回结果。
1
4
tolocalestring()
把数组转换为本地数组,并返回结果。
1
4
unshift()
向数组的开头添加一个或更多元素,并返回新的长度。
1
6
valueof()
返回数组对象的原始值
jquery ajax 请求
请求
描述
$(selector).load(url,data,callback)
把远程数据加载到被选的元素中
$.ajax(options)
把远程数据加载到 xmlhttprequest 对象中
$.get(url,data,callback,type)
使用 http get 来加载远程数据
$.post(url,data,callback,type)
使用 http post 来加载远程数据
$.getjson(url,data,callback)
使用 http get 来加载远程 json 数据
$.getscript(url,callback)
加载并执行远程的 javascript 文件
(selector) jquery 元素选择器语法
(url) 被加载的数据的 url(地址)
(data) 发送到服务器的数据的键/值对象
(callback) 当数据被加载时,所执行的函数
(type) 被返回的数据的类型 (html,xml,json,jasonp,script,text)
(options) 完整 ajax 请求的所有键/值对选项
ajax – 浏览器支持
ajax 的要点是 xmlhttprequest 对象。
不同的浏览器创建 xmlhttprequest 对象的方法是有差异的。
ie 浏览器使用 activexobject,而其他的浏览器使用名为 xmlhttprequest 的 javascript 内建对象。
如需针对不同的浏览器来创建此对象,我们要使用一条 “try and catch” 语句。您可以在我们的 javascript 教程中阅读更多有关try 和 catch 语句的内容。
让我们用这段创建 xmlhttprequest 对象的 javascript 来更新一下我们的 “testajax.htm” 文件:
<html>
<body>
<script type=”text/javascript”>
function ajaxfunction()
{
var xmlhttp;
try
{
// firefox, opera 8.0+, safari
xmlhttp=new xmlhttprequest();
}
catch (e)
{
// internet explorer
try
{
xmlhttp=new activexobject(“msxml2.xmlhttp”);
}
catch (e)
{
try
{
xmlhttp=new activexobject(“microsoft.xmlhttp”);
}
catch (e)
{
alert(“您的浏览器不支持ajax!”);
return false;
}
}
}
}
</script>
<form name=”myform”>
用户: <input type=”text” name=”username” />
时间: <input type=”text” name=”time” />
</form></body>
</html>
例子解释:
首先声明一个保存 xmlhttprequest 对象的 xmlhttp 变量。
然后使用 xmlhttp=new xmlhttprequest() 来创建此对象。这条语句针对 firefox、opera 以及 safari 浏览器。假如失败,则尝试针对 internet explorer 6.0+ 的 xmlhttp=new activexobject(“msxml2.xmlhttp”),假如也不成功,则尝试针对 internet explorer 5.5+ 的 xmlhttp=new activexobject(“microsoft.xmlhttp”)。
假如这三种方法都不起作用,那么这个用户所使用的浏览器已经太过时了,他或她会看到一个声明此浏览器不支持 ajax 的提示。
注释:上面这些浏览器定制的代码很长,也很复杂。不过,每当您希望创建 xmlhttprequest 对象时,这些代码就能派上用场,因此您可以在任何需要使用的时间拷贝粘贴这些代码。上面这些代码兼容所有的主流浏览器:internet explorer、opera、firefox 以及 safari。
下一节为您展示如何使用 xmlhttprequest 对象与服务器进行通信。
ajax – 更多有关 xmlhttprequest 对象的知识
在向服务器发送数据之前,我们有必要解释一下 xmlhttprequest 对象的三个重要的属性。
onreadystatechange 属性
onreadystatechange 属性存有处理服务器响应的函数。下面的代码定义一个空的函数,可同时对 onreadystatechange 属性进行设置:
xmlhttp.onreadystatechange=function()
{
// 我们需要在这里写一些代码
}
readystate 属性
readystate 属性存有服务器响应的状态信息。每当 readystate 改变时,onreadystatechange 函数就会被执行。
这是 readystate 属性可能的值:
状态
描述
0
请求未初始化(在调用 open() 之前)
1
请求已提出(调用 send() 之前)
2
请求已发送(这里通常可以从响应得到内容头部)
3
请求处理中(响应中通常有部分数据可用,但是服务器还没有完成响应)
4
请求已完成(可以访问服务器响应并使用它)
我们要向这个 onreadystatechange 函数添加一条 if 语句,来测试我们的响应是否已完成(意味着可获得数据):
xmlhttp.onreadystatechange=function()
{
if(xmlhttp.readystate==4)
{
// 从服务器的response获得数据
}
}
responsetext 属性
可以通过 responsetext 属性来取回由服务器返回的数据。
在我们的代码中,我们将把时间文本框的值设置为等于 responsetext:
xmlhttp.onreadystatechange=function()
{
if(xmlhttp.readystate==4)
{
document.myform.time.value=xmlhttp.responsetext;
}
}
下一节,我们会介绍如何向服务器请求数据!
ajax – 向服务器发送一个请求
要想把请求发送到服务器,我们就需要使用 open() 方法和 send() 方法。
open() 方法需要三个参数。第一个参数定义发送请求所使用的方法(get 还是 post)。第二个参数规定服务器端脚本的 url。第三个参数规定应当对请求进行异步地处理。
send() 方法可将请求送往服务器。如果我们假设 html 文件和 asp 文件位于相同的目录,那么代码是这样的:
xmlhttp.open(“get”,”time.asp”,true);
xmlhttp.send(null);
现在,我们必须决定何时执行 ajax 函数。当用户在用户名文本框中键入某些内容时,我们会令函数”在幕后”执行。
<html>
<body>
<script type=”text/javascript”>
function ajaxfunction()
{
var xmlhttp;
try
{
// firefox, opera 8.0+, safari
xmlhttp=new xmlhttprequest();
}
catch (e)
{
// internet explorer
try
{
xmlhttp=new activexobject(“msxml2.xmlhttp”);
}
catch (e)
{
try
{
xmlhttp=new activexobject(“microsoft.xmlhttp”);
}
catch (e)
{
alert(“您的浏览器不支持ajax!”);
return false;
}
}
}
xmlhttp.onreadystatechange=function()
{
if(xmlhttp.readystate==4)
{
document.myform.time.value=xmlhttp.responsetext;
}
}
xmlhttp.open(“get”,”time.asp”,true);
xmlhttp.send(null);
}
</script>
<form name=”myform”>
用户: <input type=”text” name=”username” onkeyup=”ajaxfunction();” />
时间: <input type=”text” name=”time” />
</form>
</body>
</html>
通过微软的 xml 解析器来加载 xml
微软的 xml 解析器内建于 internet explorer 5 以及更高的版本中。
下面的 javascript 片段把一个 xml 文档载入解析器中:
var xmldoc=new activexobject(“microsoft.xmldom”);
xmldoc.async=”false”;
xmldoc.load(“note.xml”);
例子解释:
上面代码的第一个行创建一个空的微软 xml 文档对象。
第二行关闭异步加载,这样确保在文档完全加载之前解析器不会继续脚本的执行。
第三行告知解析器加载名为 “note.xml” 的 xml 文档。
下面的 javascript 片段把字符串 txt 载入解析器:
var xmldoc=new activexobject(“microsoft.xmldom”);
xmldoc.async=”false”;
xmldoc.loadxml(txt);
注释:loadxml()方法用于加载字符串(文本),load() 用于加载文件。
在 firefox 及其他浏览器中的 xml 解析器
下面的 javascript 片段把 xml 文档 (“note.xml”) 载入解析器:
var xmldoc=document.implementation.createdocument(“”,””,null);
xmldoc.async=”false”;
xmldoc.load(“note.xml”);
例子解释:
上面代码的第一个行创建一个空的 xml 文档对象。
第二行关闭异步加载,这样确保在文档完全加载之前解析器不会继续脚本的执行。
第三行告知解析器加载名为 “note.xml” 的 xml 文档。
下面的 javascript 片段把字符串 txt 载入解析器:
var parser=new domparser();
var doc=parser.parsefromstring(txt,”text/xml”);
例子解释:
上面代码的第一个行创建一个空的微软 xml 文档对象。
第二行告知解析器载入名为 txt 的字符串。
注释:internet
document.getelementbyid(bdshell_js).src = http://bdimg.share.baidu.com/static/js/shell_v2.js?cdnversion= + math.ceil(new date()/3600000)
<p id="digg" articleid="25193381"> <dl id="btndigg" class="digg digg_enable" onclick="btndigga();"> <dt>顶</dt> <dd>2</dd> </dl> <dl id="btnbury" class="digg digg_enable" onclick="btnburya();"> <dt>踩</dt> <dd>0</dd> </dl> </p> <p class="tracking-ad" data-mod="popu_222"><a href="javascript:void(0);" target="_blank"> </a> </p> <p class="tracking-ad" data-mod="popu_223"> <a href="javascript:void(0);" target="_blank"> </a></p> <script type="text/javascript"> function btndigga() { $(.tracking-ad[data-mod='popu_222'] a).click(); } function btnburya() { $(.tracking-ad[data-mod='popu_223'] a).click(); } </script>
前端是庞大的,包括html、css、javascript、image、flash等等各种各样的资源。前端优化是复杂的,针对方方面面的资源都有不同的方式。那么,前端优化的目的是什么
1. 从用户角度而言,优化能够让页面加载得更快、对用户的操作响应得更及时,能够给用户提供更为友好的体验。
2. 从服务商角度而言,优化能够减少页面请求数、或者减小请求所占带宽,能够节省可观的资源。
总之,恰当的优化不仅能够改善站点的用户体验并且能够节省相当的资源利用。
前端优化的途径有很多,按粒度大致可以分为两类,第一类是页面级别的优化,例如http请求数、脚本的无阻塞加载、内联脚本的位置优化等;第二类则是代码级别的优化,例如javascript中的dom操作优化、css选择符优化、图片优化以及html结构优化等等。另外,本着提高投入产出比的目的,后文提到的各种优化策略大致按照投入产出比从大到小的顺序排列。
一、页面级优化
1. 减少http请求数
这条策略基本上所有前端人都知道,而且也是最重要最有效的。都说要减少http请求,那请求多了到底会怎么样呢?首先,每个请求都是有成本的,既包含时间成本也包含资源成本。一个完整的请求都需要经过dns寻址、与服务器建立连接、发送数据、等待服务器响应、接收数据这样一个”漫长”而复杂的过程。时间成本就是用户需要看到或者”感受”到这个资源是必须要等待这个过程结束的,资源上由于每个请求都需要携带数据,因此每个请求都需要占用带宽。另外,由于浏览器进行并发请求的请求数是有上限的(具体参见此处),因此请求数多了以后,浏览器需要分批进行请求,因此会增加用户的等待时间,会给用户造成站点速度慢这样一个印象,即使可能用户能看到的第一屏的资源都已经请求完了,但是浏览器的进度条会一直存在。
减少http请求数的主要途径包括:
(1). 从设计实现层面简化页面
如果你的页面像百度首页一样简单,那么接下来的规则基本上都用不着了。保持页面简洁、减少资源的使用时最直接的。如果不是这样,你的页面需要华丽的皮肤,则继续阅读下面的内容。
(2). 合理设置http缓存
缓存的力量是强大的,恰当的缓存设置可以大大的减少http请求。以有啊首页为例,当浏览器没有缓存的时候访问一共会发出78个请求,共600多k数据(如图1.1),而当第二次访问即浏览器已缓存之后访问则仅有10个请求,共20多k数据(如图1.2)。(这里需要说明的是,如果直接f5刷新页面的话效果是不一样的,这种情况下请求数还是一样,不过被缓存资源的请求服务器是304响应,只有header没有body,可以节省带宽)
怎样才算合理设置?原则很简单,能缓存越多越好,能缓存越久越好。例如,很少变化的图片资源可以直接通过http header中的expires设置一个很长的过期头;变化不频繁而又可能会变的资源可以使用last-modifed来做请求验证。尽可能的让资源能够在缓存中待得更久。关于http缓存的具体设置和原理此处就不再详述了,有兴趣的可以参考下列文章:
http1.1协议中关于缓存策略的描述
fiddler http performance中关于缓存的介绍
(3). 资源合并与压缩
如果可以的话,尽可能的将外部的脚本、样式进行合并,多个合为一个。另外,css、javascript、image都可以用相应的工具进行压缩,压缩后往往能省下不少空间。
(4). css sprites
合并css图片,减少请求数的又一个好办法。
(5). inline images
使用data: url scheme的方式将图片嵌入到页面或css中,如果不考虑资源管理上的问题的话,不失为一个好办法。如果是嵌入页面的话换来的是增大了页面的体积,而且无法利用浏览器缓存。使用在css中的图片则更为理想一些
(6). lazy load image
这条策略实际上并不一定能减少http请求数,但是却能在某些条件下或者页面刚加载时减少http请求数。对于图片而言,在页面刚加载的时候可以只加载第一屏,当用户继续往后滚屏的时候才加载后续的图片。这样一来,假如用户只对第一屏的内容感兴趣时,那剩余的图片请求就都节省了。有啊首页曾经的做法是在加载的时候把第一屏之后的图片地址缓存在textarea标签中,待用户往下滚屏的时候才”惰性”加载。
2. 将外部脚本置底
前文有谈到,浏览器是可以并发请求的,这一特点使得其能够更快的加载资源,然而外链脚本在加载时却会阻塞其他资源,例如在脚本加载完成之前,它后面的图片、样式以及其他脚本都处于阻塞状态,直到脚本加载完成后才会开始加载。如果将脚本放在比较靠前的位置,则会影响整个页面的加载速度从而影响用户体验。解决这一问题的方法有很多,在这里有比较详细的介绍(这里是译文和更详细的例子),而最简单可依赖的方法就是将脚本尽可能的往后挪,减少对并发下载的影响。
3. 异步执行inline脚本
inline脚本对性能的影响与外部脚本相比,是有过之而无不及。首页,与外部脚本一样,inline脚本在执行的时候一样会阻塞并发请求,除此之外,由于浏览器在页面处理方面是单线程的,当inline脚本在页面渲染之前执行时,页面的渲染工作则会被推迟。简而言之,inline脚本在执行的时候,页面处于空白状态。鉴于以上两点原因,建议将执行时间较长的inline脚本异步执行,异步的方式有很多种,例如使用script元素的defer属性(存在兼容性问题和其他一些问题,例如不能使用document.write)、使用settimeout,此外,在html5中引入了web workers的机制,恰恰可以解决此类问题
4. lazy load javascript
随着javascript框架的流行,越来越多的站点也使用起了框架。不过,一个框架往往包括了很多的功能实现,这些功能并不是每一个页面都需要的,如果下载了不需要的脚本则算得上是一种资源浪费-既浪费了带宽又浪费了执行花费的时间。目前的做法大概有两种,一种是为那些流量特别大的页面专门定制一个专用的mini版框架,另一种则是lazy load。yui则使用了第二种方式,在yui的实现中,最初只加载核心模块,其他模块可以等到需要使用的时候才加载
5. 将css放在head中
如果将css放在其他地方比如body中,则浏览器有可能还未下载和解析到css就已经开始渲染页面了,这就导致页面由无css状态跳转到css状态,用户体验比较糟糕。除此之外,有些浏览器会在css下载完成后才开始渲染页面,如果css放在靠下的位置则会导致浏览器将渲染时间推迟。
6. 异步请求callback
在某些页面中可能存在这样一种需求,需要使用script标签来异步的请求数据。类似:
javascript:
/*callback函数*/
function mycallback(info){
//do something here
}
html:
<script type=”text/javascript” src=”http://abc.com/cb”></script>
cb返回的内容:
mycallback(‘hello world!’);
像以上这种方式直接在页面上写<script>对页面的性能也是有影响的,即增加了页面首次加载的负担,推迟了domloaded和window.onload事件的触发时机。如果时效性允许的话,可以考虑在domloaded事件触发的时候加载,或者使用settimeout方式来灵活的控制加载的时机。
7. 减少不必要的http跳转
对于以目录形式访问的http链接,很多人都会忽略链接最后是否带’/’,假如你的服务器对此是区别对待的话,那么你也需要注意,这其中很可能隐藏了301跳转,增加了多余请求。具体参见下图,其中第一个链接是以无’/’结尾的方式访问的,于是服务器有了一次跳转。
8. 避免重复的资源请求
这种情况主要是由于疏忽或页面由多个模块拼接而成,然后每个模块中请求了同样的资源时,会导致资源的重复请求
二、代码级优化
1. javascript
(1). dom
dom操作应该是脚本中最耗性能的一类操作,例如增加、修改、删除dom元素或者对dom集合进行操作。如果脚本中包含了大量的dom操作则需要注意以下几点:
a. html collection
在脚本中document.images、document.forms、getelementsbytagname()返回的都是htmlcollection类型的集合,在平时使用的时候大多将它作为数组来使用,因为它有length属性,也可以使用索引访问每一个元素。不过在访问性能上则比数组要差很多,原因是这个集合并不是一个静态的结果,它表示的仅仅是一个特定的查询,每次访问该集合时都会重新执行这个查询从而更新查询结果。所谓的”访问集合”包括读取集合的length属性、访问集合中的元素。
因此,当你需要遍历html collection的时候,尽量将它转为数组后再访问,以提高性能。即使不转换为数组,也请尽可能少的访问它,例如在遍历的时候可以将length属性、成员保存到局部变量后再使用局部变量。
b. reflow & repaint
除了上面一点之外,dom操作还需要考虑浏览器的reflow和repaint,因为这些都是需要消耗资源的,具体的可以参加以下文章:
如何减少浏览器的repaint和reflow?
understanding internet explorer rendering behaviour
notes on html reflow
(2). 慎用with
with(obj){ p = 1}; 代码块的行为实际上是修改了代码块中的执行环境,将obj放在了其作用域链的最前端,在with代码块中访问非局部变量是都是先从obj上开始查找,如果没有再依次按作用域链向上查找,因此使用with相当于增加了作用域链长度。而每次查找作用域链都是要消耗时间的,过长的作用域链会导致查找性能下降。
因此,除非你能肯定在with代码中只访问obj中的属性,否则慎用with,替代的可以使用局部变量缓存需要访问的属性。
(3). 避免使用eval和function
每次 eval 或 function 构造函数作用于字符串表示的源代码时,脚本引擎都需要将源代码转换成可执行代码。这是很消耗资源的操作 —— 通常比简单的函数调用慢100倍以上。
eval 函数效率特别低,由于事先无法知晓传给 eval 的字符串中的内容,eval在其上下文中解释要处理的代码,也就是说编译器无法优化上下文,因此只能有浏览器在运行时解释代码。这对性能影响很大。
function 构造函数比eval略好,因为使用此代码不会影响周围代码;但其速度仍很慢。
此外,使用eval和function也不利于javascript压缩工具执行压缩。
(4). 减少作用域链查找
前文谈到了作用域链查找问题,这一点在循环中是尤其需要注意的问题。如果在循环中需要访问非本作用域下的变量时请在遍历之前用局部变量缓存该变量,并在遍历结束后再重写那个变量,这一点对全局变量尤其重要,因为全局变量处于作用域链的最顶端,访问时的查找次数是最多的。
低效率的写法:
//全局变量
var globalvar = 1;
function mycallback(info){
for( var i = 100000; i–;){
//每次访问globalvar都需要查找到作用域链最顶端,本例中需要访问100000次
globalvar += i;
}
}
更高效的写法:
//全局变量
var globalvar = 1;
function mycallback(info){
//局部变量缓存全局变量
var localvar = globalvar;
for( var i = 100000; i–;){
//访问局部变量是最快的
localvar += i;
}
//本例中只需要访问2次全局变量
globalvar = localvar;
}
此外,要减少作用域链查找还应该减少闭包的使用。
(5). 数据访问
javascript中的数据访问包括直接量(字符串、正则表达式)、变量、对象属性以及数组,其中对直接量和局部变量的访问是最快的,对对象属性以及数组的访问需要更大的开销。当出现以下情况时,建议将数据放入局部变量:
a. 对任何对象属性的访问超过1次
b. 对任何数组成员的访问次数超过1次
另外,还应当尽可能的减少对对象以及数组深度查找。
(6). 字符串拼接
在javascript中使用”+”号来拼接字符串效率是比较低的,因为每次运行都会开辟新的内存并生成新的字符串变量,然后将拼接结果赋值给新变量。与之相比更为高效的做法是使用数组的join方法,即将需要拼接的字符串放在数组中最后调用其join方法得到结果。不过由于使用数组也有一定的开销,因此当需要拼接的字符串较多的时候可以考虑用此方法。
关于javascript优化的更详细介绍请参考:
write efficient javascript(ppt)
efficient javascript
2. css选择符
在大多数人的观念中,都觉得浏览器对css选择符的解析式从左往右进行的,例如
#toc a { color: #444; }
这样一个选择符,如果是从右往左解析则效率会很高,因为第一个id选择基本上就把查找的范围限定了,但实际上浏览器对选择符的解析是从右往左进行的。如上面的选择符,浏览器必须遍历查找每一个a标签的祖先节点,效率并不像之前想象的那样高。根据浏览器的这一行为特点,在写选择符的时候需要注意很多事项,有人已经一一列举了,详情参考此处。
3. html
对html本身的优化现如今也越来越多的受人关注了,详情可以参见这篇总结性文章。
4. image压缩
图片压缩是个技术活,不过现如今这方面的工具也非常多,压缩之后往往能带来不错的效果,具体的压缩原理以及方法在《even faster web sites》第10章有很详细的介绍,有兴趣的可以去看看。
总结
本文从页面级以及代码级两个粒度对前端优化的各种方式做了一个总结,这些方法基本上都是前端开发人员在开发的过程中可以借鉴和实践的,除此之外,完整的前端优化还应该包括很多其他的途径,例如cdn、gzip、多域名、无cookie服务器等等,由于对于开发人员的可操作性并不强大,在此也就不多叙述了,详细的可以参考yahoo和google的这些”金科玉律”。1.javascript简介
html是纯静态的的页面,而javascript让页面有了动态的效果,比如;oa中模块的拖拉
所有的浏览器都会内置javascript的解释器
1992年 nombas公司开发出c减减的嵌入式脚本语言。这是最好的html页面的脚本语言。
netscape为了扩展其浏览器的功能,开发了一套livescript,并与1995年与sun公司联合把其改名为javascript,它的主要目的是处理一些输入的有效性验证,而之前这个工作是留给perl之类的服务器端语言完成,在以前使用电话线调制解调器(28.8kb/s)的时代,如此慢的与服务器交互,这绝对是一件很痛苦的事情。javascript的出现,解决了这个问题,因为它的验证是基于客户端的。
微软公司早期版本的浏览器仅支持自己的vbscript,但后来不得不加入javascript
ie3中搭载javascipt的克隆版本,命名为jscript
在一次技术会议中,sun,microsoft,netscape公司联合制定了ecma-script标准
在2005前,网页上提示框,广告越来越多,把javascipt滥用,使javascript背上了大量的罪名。
2005年,google公司的网上产品(google地图,gmail,google搜索建议)等使得ajax兴起,而javascript便是ajax最重要的元素之一
javascript有三个部分组成
ecmascript dom bom
web标准
网页主要有三部分组成
(结构html,xhtml,表现css,行为dom,ecma)
2.ecma脚本
javascript的语法
区分大小写
弱类型变量 var age=10 var name=”dd”
每行结尾的分号可有可无,但建议还是加上
注释与java相同
变量
变量是通过var关键字来声明的。(variable)
变量的命名规则与java一致
注释有三种:// /**/ <!– –>这个只能注释单行
2.1 javascript的hello world
document.write()是写在文档的最前面
2.2 slice()、substring()、subtr
slice和substring (2,5) 指的是从第3为开始,取(5-2)=3个数,其中slice的参数可以为负
substr(2,5)指的是从第3为开始,取5个数。但ecmascript 没有对该方法进行标准化,因此尽量少使用该方法
2.3 indexof()和lastindexof(),isnan,typeof
indexof(”i”) //从前往后,i在第几位
indexof(”i”,3)可选参数,从第几个字符开始往后找
lastindexof(”i”) //从后往前,i在第几位
lastindexof(“i”,3) //从后往前,i在第几位
如果没找到,则返回-1
string类型的变量,在java中,用””符号表示字符串,用”表示单个字符。而在javascript中这两种都可以
nan(not a number)
alert(nan ==nan)返回false,因此不推荐使用nan本身,推荐函数isnan
alert(isnann(“ab”));//返回false
typeof运算符
var stmp = “test”;
alert(typeof stmp); //输出string
alert(type of 1);//输出number
此外:还有boolean,undefined,object(如果是引用类型或者null值,null值返回object,这其实是ecmascript的一个错误)
当声明的变量未初始化的时候,它的值就是undefined.当没有这个变量的时候,typeof 变
返回的值也是undefined。但是没声明的变量是不能参与计算的。
当函数无明确返回值时,返回的也是undefined
function a(){
}
alert(a() == undefined) //返回true
alert(null == undefined)//返回true
2.4 数值计算
var mynum1 = 23.345;
var mynum2 = 45;
var mynum3 = -34;
var mynum4 = 9e5; //科学计数法为 9*10五次方
var fnumber = 123.456;
alert(fnumber.toexponential(1));//保留的小数点数 1.2e+2
alert(fnumber.toexponential(2));//1.23e+2
.5 布尔值
var married = true;
alert(“1.” + typeof(married));//boolean
married = “true”;
alert(“2.” + typeof(married));//string
.6 类型转换
转换成string类型有三种方式
var a = 3;
var b = a + “”;
var c = a.tostring();
var d = “student” + a;
tostring()
var a=11;
document.write(a.tostring(2) + “<br>”);//转成2进制
document.write(a.tostring(3) + “<br>”);//转成3进制
如果不是数值,则转换报错
parseint()
document.write(parseint(“1red6″) + “<br>”);//返回1,后面非数值的将全部忽略
document.write(parseint(“53.5″) + “<br>”);//返回53
document.write(parseint(“0xc”) + “<br>”); //直接十进制转换12
document.write(parseint(“isaacshun@gmail.com”) + “<br>”);//nan
document.write(parseint(“011″,8) + “<br>”);返回9
document.write(parseint(“011″,10) + “<br>”); //指定为十进制返回11
parsefloat()与parseint()类似
2.7 数组
var amap = new array(“china”,”usa”,”britain”);
amap[20] = “korea”;
alert(amap.length + ” ” + amap[10] + ” ” + amap[20]);//amap[10]返回undefined
document.write(amap.join(“][“) + “<br>”); //用”][“来连接
var sfruit = “apple,pear,peach,orange”;
var afruit = sfruit.split(“,”);
var afruit = [“apple”,”pear”,”peach”,”orange”];
alert(afruit.reverse().tostring());
var afruit = [“pear”,”apple”,”peach”,”orange”];
afruit.sort();
var stack = new array();
stack.push(“red”);
stack.push(“green”);
stack.push(“blue”);
document.write(stack.tostring() + “<br>”);
var vitem = stack.pop(); // blue
document.write(vitem + “<br>”);
document.write(stack.tostring()); // red green
2.8 if语句
//首先获取用户的一个输入,并用number()强制转换为数字
var inumber = number(prompt(“输入一个5到100之间的数字”, “”));//第二个参数,用于显示输入框的默认值
if(isnan(inumber)) //判断输入的是否是数字nan “not a number”
document.write(“请确认你的输入正确”);
else if(inumber > 100 || inumber -1 || suseragent.indexof(“applewebkit”) > -1;
//检测ie、mozilla
var isie = suseragent.indexof(“compatible”) > -1 && suseragent.indexof(“msie”) > -1 && !isopera;
var ismoz = suseragent.indexof(“gecko”) > -1 && !iskhtml;
//检测操作系统
var iswin = (navigator.platform == “win32″) || (navigator.platform == “windows”);
var ismac = (navigator.platform == “mac68k”) || (navigator.platform == “macppc”) || (navigator.platform == “macintosh”);
var isunix = (navigator.platform == “x11″) && !iswin && !ismac;
if(isopera) document.write(“opera “);
if(iskhtml) document.write(“khtml “);
if(isie) document.write(“ie “);
if(ismoz) document.write(“mozilla “);
if(iswin) document.write(“windows”);
if(ismac) document.write(“mac”);
if(isunix) document.write(“unix”);
global对象
其实isnan,paraseint等都是global对象的方法
encodeuri.因为有效的uri不能包含某些字符,如空格。这个方法就是用于将这个字符转换成utf-8编码,使浏览器可以接受他们。
var suil = “www.oseschool.com/pro file/a.html”;
alert(encodeuri(suil));//www.oseschool.com/pro%20file/a.html
即将空格编码成%20
eval方法
eval(“alert(‘hi’)”);
当解析程序发现eval()时,它将把参数解析成真正的ecma-script语句,然后插入该语句所在位置。
global除了有内置方法外,还有很多内置的属性:如:undefined,nan,array,string,number,date,regexp等
math对象
max方法,min方法,ceil,floor,round,sqrt,random
max(1,2,3);min(1.2,3.4);
想取到1~10的数据
math.floor(math.random()*10+1)
2~9的数据
math.floor(math.random()*9+2);
3.1 getelementsbytagname
function searchdom(){
//放在函数内,页面加载完成后才用<body>的onload加载,这时如果把alert这句改成用document.write则会把原内容覆盖掉,因为是后面才执行的
var oli = document.getelementsbytagname(“li”);
//输出长度、标签名称以及某项的文本节点值
alert(oli.length + ” ” +oli[0].tagname + ” ” + oli[3].childnodes[0].nodevalue);
var oul = document.getelementsbytagname(“ul”);
var oli2 = oul[1].getelementsbytagname(“li”);
alert(oli2.length + ” ” +oli2[0].tagname + ” ” + oli2[1].childnodes[0].nodevalue);
}
<body onload=”searchdom()”>
<ul>客户端语言
<li>html</li>
<li>javascript</li>
<li id=”cssli”>css</li>
</ul>
<ul>服务器端语言
<li>asp.net</li>
<li>jsp</li>
<li>php</li>
</ul>
</body>
3.2 getelementbyid
var oli = document.getelementbyid(“cssli”);
oli.style .backgroundcolor=”yellow”
//输出标签名称以及文本节点值
alert(oli.tagname + ” ” + oli.childnodes[0].nodevalue);
3.2 getelementsbyname
alert(document.getelementsbyname(“a”).length);
3.3 访问子节点
function mydominspector(){
var oul = document.getelementbyid(“mylist”); //获取<ul>标记
var domstring = “”;
if(oul.haschildnodes()){ //判断是否有子节点
var och = oul.childnodes;
for(var i=0;i<och.length;i++) //逐一查找
domstring += och[i].nodename + “\n”;
}
alert(domstring);
}
4访问父节点
nodename如果为文本节点,则返回#text
tagname如果为文本节点,则返回undefined
function mydominspector(){
var myitem = document.getelementbyid(“mydearfood”);
alert(myitem.parentnode.tagname);
}
function mydominspector(){
var myitem = document.getelementbyid(“mydearfood”);
var parentelm = myitem.parentnode;
while(parentelm.classname != “colorful” && parentelm != document.body)
parentelm = parentelm.parentnode; //一路往上找
alert(parentelm.tagname);
}
<body onload=”mydominspector()”>
<p class=”colorful”>
<ul>
<li>糖醋排骨</li>
<li>圆笼粉蒸肉</li>
<li>泡菜鱼</li>
<li id=”mydearfood”>板栗烧鸡</li>
<li>麻婆豆腐</li>
</ul>
</p>
</body>
访问兄弟节点
function mydominspector(){
var myitem = document.getelementbyid(“mydearfood”);
//访问兄弟节点
var nextlistitem = myitem.nextsibling;
var prelistitem = myitem.previoussibling;
alert(nextlistitem.tagname +” “+ prelistitem.tagname);
}
在firefox中不支持,但是ie中却是支持的。
3.6 第一个最后一个子节点
function nextsib(node){
var templast = node.parentnode.lastchild;
//判断是否是最后一个节点,如果是则返回null
if(node == templast)
return null;
var tempobj = node.nextsibling;
//逐一搜索后面的兄弟节点,直到发现元素节点为止
while(tempobj.nodetype!=1 && tempobj.nextsibling!=null)
tempobj = tempobj.nextsibling;
//三目运算符,如果是元素节点则返回节点本身,否则返回null
return (tempobj.nodetype==1)?tempobj:null;
}
function prevsib(node){
var tempfirst = node.parentnode.firstchild;
//判断是否是第一个节点,如果是则返回null
if(node == tempfirst)
return null;
var tempobj = node.previoussibling;
//逐一搜索前面的兄弟节点,直到发现元素节点为止
while(tempobj.nodetype!=1 && tempobj.previoussibling!=null)
tempobj = tempobj.previoussibling;
return (tempobj.nodetype==1)?tempobj:null;
}
function mydominspector(){
var myitem = document.getelementbyid(“mydearfood”);
//获取后一个元素兄弟节点
var nextlistitem = nextsib(myitem);
//获取前一个元素兄弟节点
var prelistitem = prevsib(myitem);
alert(“后一项:” + ((nextlistitem!=null)?nextlistitem.firstchild.nodevalue:null) + ” 前一项:” + ((prelistitem!=null)?prelistitem.firstchild.nodevalue:null) );
}
nodetype
元素element
1
属性attr
2
文本text
3
注释comments
8
文档document
9
function showattr(){
var btnshowattr=document.getelementbyid(“btnshowattr”); //演示按钮,有很多属性
var attrs=btnshowattr.attributes;
for(var i=0;i<attrs.length ;i++){
var attr=attrs[i];
alert(‘nodetype:’+attr.nodetype); //attribute 的nodetype=2
alert(‘attr:’+attr);
alert(‘attr.name:’+attr.name+’=’+attr.value);
}
function showdocument(){
alert(‘nodetype:’+document.nodetype); //9
alert(‘nodename:’+document.nodename);
alert(document);
}
3.7 getattribute setattribute
function mydominspector(){
//获取图片
var myimg = document.getelementsbytagname(“img”)[0];
//获取图片title属性
alert(myimg.getattribute(“title”));
}
function changepic(){
//获取图片
var myimg = document.getelementsbytagname(“img”)[0];
//设置图片src和title属性
myimg.setattribute(“src”,”02.jpg”);
myimg.setattribute(“title”,”紫荆公寓”);
}
3.8创建新节点
function createp(){
var op = document.createelement(“p”);
var otext = document.createtextnode(“hhhhh”);
op.appendchild(otext);
op.setattribute(“style”,”text-align:center”);
document.body.appendchild(op);
//创建完节点,就马上会影响到下面的操作,比如p的数量就会多1个
}
3.9删除节点
需要注意的是标签之间的父子关系!!!
function deletep(){
var op = document.getelementsbytagname(“p”)[0];
op.parentnode.removechild(op); //删除节点
}
3.10替换节点
function replacep(){
var ooldp = document.getelementsbytagname(“p”)[0];
var onewp = document.createelement(“p”); //新建节点
var otext = document.createtextnode(“这是一个感人肺腑的故事”);
onewp.appendchild(otext);
ooldp.parentnode.replacechild(onewp,ooldp); //替换节点
}
3.11插入节点
function insertp(){
var ooldp = document.getelementsbytagname(“p”)[0];
var onewp = document.createelement(“p”); //新建节点
var otext = document.createtextnode(“这是一个感人肺腑的故事”);
onewp.appendchild(otext);
ooldp.parentnode.insertbefore(onewp,ooldp); //插入节点
}
没有insertafter,但是可以自己写一个
function insertafter(newelement, targetelement){
var oparent = targetelement.parentnode; //首先找到目标元素的父元素
if(oparent.lastchild == targetelement) //如果目标元素已经是最后一个子元素了
oparent.appendchild(newelement); //则直接用appendchild()加到子元素列表的最后
else //否则用insertbefore()插入到目标元素的下一个兄弟元素之前
oparent.insertbefore(newelement,targetelement.nextsibling);
}
function insertp(){
var ooldp = document.getelementbyid(“mytarget”);
var onewp = document.createelement(“p”); //新建节点
var otext = document.createtextnode(“这是一个感人肺腑的故事”);
onewp.appendchild(otext);
insertafter(onewp,ooldp); //插入节点
}
其实这个也是通过insertbefore原理来实现的
3.12 clonenode(deepboolean)
复制并返回当前节点的复制节点,这个复制得到的节点是一个孤立的节点,不在document树中。复制原来节点的属性值,包括id属性,所以在把这个新节点加到document之前,一定要修改id属性,以便使它保持唯一。当然如果id的唯一性不重要可以不做处理。
这个方法支持一个布尔参数,当deepboolean设置true时,复制当前节点的所有子节点,包括该节点内的文本。
<p id=”mypara”>11111</p>
p=document.getelementbyid(“mypara”)
pclone = p.clonenode(true);
p.parentnode.appendchild(pclone
3.12文档碎片
function insertps(){
var acolors = [“red”,”green”,”blue”,”magenta”,”yellow”,”chocolate”,”black”,”aquamarine”,”lime”,”fuchsia”,”brass”,”azure”,”brown”,”bronze”,”deeppink”,”aliceblue”,”gray”,”copper”,”coral”,”feldspar”,”orange”,”orchid”,”pink”,”plum”,”quartz”,”purple”];
var ofragment = document.createdocumentfragment(); //创建文档碎片
for(var i=0;i<acolors.length;i++){
var op = document.createelement(“p”);
var otext = document.createtextnode(acolors[i]);
op.appendchild(otext);
ofragment.appendchild(op); //将节点先添加到碎片中
}
document.body.appendchild(ofragment); //最后一次性添加到页面
}
3.13 innerhtml
function mydominnerhtml(){
var myp = document.getelementbyid(“mytest”);
alert(myp.innerhtml); //直接显示innerhtml的内容
//修改innerhtml,可直接添加代码
myp.innerhtml = “<img src=’01.jpg’ title=’情人坡’>”;
}
innerhtml可同时显示没有的代码
3.14 换皮肤
<style type=”text/css”>
.myul1{
color:#0000ff;
font-family:arial;
font-weight:bold;
}
.myul2{
color:#ff0000;
font-family:georgia, “times new roman”, times, serif;
}
</style>
<script language=”javascript”>
function check(){
var omy = document.getelementsbytagname(“ul”)[0];
omy.classname =(omy.classname==”myul1″? “myul2″:”myul1″); //修改css类
}
</script>
</head>
<body>
<ul onclick=”check()” class=”myul1″>
<li>html</li>
<li>javascript</li>
<li>css</li>
</ul>
</body>
4.表格与表单
4.1 动态添加行
<script language=”javascript”>
window.onload=function(){
var otr = document.getelementbyid(“member”).insertrow(2); //插入一行
var atext = new array();
atext[0] = document.createtextnode(“fresheggs”);
atext[1] = document.createtextnode(“w610″);
atext[2] = document.createtextnode(“nov 5th”);
atext[3] = document.createtextnode(“scorpio”);
atext[4] = document.createtextnode(“1038818″);
for(var i=0;i
otd.appendchild(atext[i]);
}
}
</script>
4.2修改单元格内容
<script language=”javascript”>
window.onload=function(){
var otable = document.getelementbyid(“member”);
otable.rows[3].cells[4].innerhtml = “lost”; //修改单元格内容
}
</script>
4.3 动态删除
parentelement是ie dom,
parentnode是标准dom
<script language=”javascript”>
window.onload=function(){
var otable = document.getelementbyid(“member”);
otable.deleterow(2); //删除一行,后面的行号自动补齐//指从table中的第2行开始进行删除
otable.rows[2].deletecell(1); //删除一个单元格,后面的也自动补齐
}
</script>
<script language=”javascript”>
function mydelete(){
var otable = document.getelementbyid(“member”);
//删除该行
this.parentnode.parentnode.parentnode.removechild(this.parentnode.parentnode);
}
window.onload=function(){
var otable = document.getelementbyid(“member”);
var otd;
//动态添加delete链接
for(var i=1;i<otable.rows.length;i++){
otd = otable.rows[i].insertcell(5);
otd.innerhtml = “<a href=’#’>delete</a>”;
otd.firstchild.onclick = mydelete; //添加删除事件
}
}
</script>
4.4动态删除列
<script language=”javascript”>
function deletecolumn(otable,inum){
//自定义删除列函数,即每行删除相应单元格
for(var i=0;i
}
window.onload=function(){
var otable = document.getelementbyid(“member”);
deletecolumn(otable,2);
}
</script>
4.5 控制textarea的字符个数
<script language=”javascript”>
function lessthan(otextarea){
//返回文本框字符个数是否符号要求的boolean值
return otextarea.value.length fnclick1
但是以上的监听器均为ie中的标准,而符合标准dom(firefox)的监听器如下
op.addeventlistener(“click”,fnclick1,false); //添加监听函数1
op.addeventlistener(“click”,fnclick2,false); //添加监听函数2
因此这种方式在firefox中支持,而在ie中不支持
为了兼容性,可这样写
if (el.addeventlistener)…{
el.addeventlistener(‘click’, kinddisablemenu, false);
} else if (el.attachevent)…{
el.attachevent(‘onclick’, kinddisablemenu);
}
第三个参数为usecapture
而usecapture這個參數就是在控制這時候兩個click事件的先後順序。如果是false,那就會使用bubbling,他是從內而外的流程,所以會先執行藍色元素的click事件再執行紅色元素的click事件,如果是true,那就是capture,和bubbling相反是由外而內
6.3 事件的类型event.type
ie浏览器中事件对象是window对象的一个属性event
op.onlick=function(){ var o = window.event}
而标准dom中规定event对象必须作为唯一的参数传给事件处理函数
op.onclick=function(oevent){
}
因此为了兼容两种浏览器,通常采用下面的方法
op.onclick=function(o){
if(window.event)//假如不等于空,则为ie浏览器
o = window.event;
}
<script language=”javascript”>
function handle(oevent){
var op = document.getelementbyid(“display”);
if(window.event) oevent = window.event; //处理兼容性,获得事件对象
if(oevent.type == “click”) //检测事件名称
op.innerhtml += “你点击了我  ”;
else if( oevent.type == “mouseover”)
op.innerhtml += “你移动到我上方了  ”;
}
window.onload = function(){
var oimg = document.getelementsbytagname(“img”)[0];
oimg.onclick = handle;
oimg.onmouseover = handle;
}
</script>
还有很多鼠标事件
window.onload = function(){
var oimg = document.getelementsbytagname(“img”)[0];
oimg.onmousedown = handle; //将鼠标事件除了mousemove外都监听
oimg.onmouseup = handle;
oimg.onmouseover = handle;
oimg.onmouseout = handle;
oimg.onclick = handle;
oimg.ondblclick = handle;
}
6.4 事件的激活元素event.srcelement 或者 target
<script language=”javascript”>
function handle(oevent){
if(window.event) oevent = window.event; //处理兼容性,获得事件对象
var otarget;
if(oevent.srcelement) //处理兼容性,获取事件目标
otarget = oevent.srcelement; //ie支持的写法
else
otarget = oevent.target; //firefox支持的写法
alert(otarget.tagname); //弹出目标的标记名称
}
window.onload = function(){
var oimg = document.getelementsbytagname(“img”)[0];
oimg.onclick = handle;
}
</script>
event.button
<script language=”javascript”>
function testclick(oevent){
var op = document.getelementbyid(“display”);
if(window.event)
oevent = window.event;
op.innerhtml += oevent.button; //输出button的值
}
document.onmousedown = testclick;
window.onload = testclick; //测试未按下任何键
</script>
</head>
<body>
<p id=”display”></p>
</body>
在ie/opera中,是window.event,而在firefox中,是event
而事件的对象,在ie中是window.event.srcelement,在firefox中是event.target,而在opera中则两者都支持。
在 ie 里面
左键是window.event.button=1
右键是window.event.button=2
中键是window.event.button=4
没有按键动作的时候window.event.button=0
在 firefox 里面
左键是event.button=0
右键是event.button=2
中键是event.button=1
没有按键动作的时候event.button=0
在 opera 7.23/7.54 里面
鼠标左键是window.event.button=1
没有按键动作的时候window.event.button=1
右键和中键无法获取
键盘事件
window.onload = function(){
var otextarea = document.getelementsbytagname(“textarea”)[0];
otextarea.onkeydown = handle; //监听所有键盘事件
otextarea.onkeyup = handle;
otextarea.onkeypress = handle;
}
e.keycode;
onkeypress是在用户按下并放开任何字母数字键时发生。系统按钮(例如,箭头键和功能键, shift、ctrl、alt、f1、f2)无法得到识别。
onkeyup 是在用户放开任何先前按下的键盘键时发生。
onkeydown 是在用户按下任何键盘键(包括系统按钮,如箭头键和功能键)时发生
屏蔽鼠标右键
第一种方式:
<script language=”javascript”>
function block(oevent){
if(window.event)
oevent = window.event;
if(oevent.button == 2)
alert(“鼠标右键不可用”);
}
document.onmousedown = block;
</script>
第二种方式:
<script language=”javascript”>
function block(oevent){
if(window.event){
oevent = window.event;
oevent.returnvalue = false; //取消默认事件支持ie
}else
oevent.preventdefault(); //取消默认事件支持firefox
}
document.oncontextmenu = block;
</script>
6.8伸缩的菜单
<script language=”javascript”>
function change(){
//通过父元素li,找到兄弟元素ul
var osecondp = this.parentnode.getelementsbytagname(“ul”)[0];//这里的this就是下面的oa
//css交替更换来实现显、隐
if(osecondp.classname == “myhide”)
osecondp.classname = “myshow”;
else
osecondp.classname = “myhide”;
}
window.onload = function(){
var oul = document.getelementbyid(“listul”);
var ali = oul.childnodes; //子元素
var oa;
for(var i=0;i禁用调试,显示脚本错误
firefox错误控制台
microsoft script debugger
venkman firefox的插件
6.4 javascript优化
1.提高javascript下载时间。将javascript写到同一行
2.尽量使用内置函数(因为内置函数是通过c语言编译到浏览器中的)
.实例
1 图片查看器
<html>
<head>
<title></title>
<script>
function showpic(obj){
var h = obj.getattribute(“href”);
document.getelementbyid(“image”).setattribute(“src”,h);
}
</script>
</head>
<body>
<h1>snapshots</h1>
<ul>
<li><a href=”photo/01.jpg” title=”a” onclick=”showpic(this);return false;“>01</a></li>
<li><a href=”photo/02.jpg” title=”b” onclick=”showpic(this);return false;”>02</a></li>
<li><a href=”photo/03.jpg” title=”c” onclick=”showpic(this);return false;”>03</a></li>
<li><a href=”photo/04.jpg” title=”d” onclick=”showpic(this);return false;”>04</a></li>
<li><a href=”photo/05.jpg” title=”e” onclick=”showpic(this);return false;”>05</a></li>
</ul>
<img id=”image” src=”photo/01.jpg” alt=”my image”></img>
</body>
</html>
return false指的是把默认的noclick事件取消
给其加上css
<style>
body{
color:#333;
background:#ccc;
margin:1em 10%;
}
a{
text-decoration:none;
padding:10px;
color:#c60;
}
a:link, a:visited{
color: #a62020; background-color: #ecd8db; text-decoration: none; padding:4px 10px 4px 10px;
border-top:1px solid #eee;
border-left:1px solid #eee;
border-bottom:1px solid #717171;
border-right:1px solid #717171;
}
a:hover{
color:#821818; background-color:#e2c4c9; padding:5px 8px 3px 12px;
border-top:1px solid #717171;
border-left:1px solid #717171;
border-bottom:1px solid #eee;
border-right:1px solid #eee;}
ul{
margin:0px;
padding:0px;
}
li{
list-style-type:none;
display:inline;
}
img{
margin:10px 0px;
}
</style>
当时现在有个缺陷,就是onclick的事件直接写在了html上,分离
先给ul加上个属性id<ul id=”img_ul”>
window.onload = preparegalley;
function preparegalley(){
var img_ul = document.getelementbyid(“img_ul”);
var links = img_ul.getelementsbytagname(“a”);
for(var i=0;i<links.length;i++){
links[i].onclick = function(){
showpic(this);
return false;
}
}
}
有一个问题,如果onload的函数有多个怎么办?
window.onload = preparegalley1;
window.onload = preparegalley2;
显然,这样第一个函数就会被第二个函数覆盖。
可以这样写
window.onload = function(){
preparegalley1();
preparegalley2();
}
还有一个比这个更nb的写法,由simon willison写的
function addloadevent(func){
var oldonload = window.onload;
if(typeof window.onload !=’function’){
window.onload = func;
}else{
window.onload = function(){
oldonload();
func();
}
}
}
addloadevent(preparegalley1);
addloadevent(preparegalley2);
编写一个方法求一个字符串的字节长度
new function(s){
if(!arguments.length||!s) return null;
if(“”==s) return 0;
var l=0;
for(var i=0;i<s.length;i++){
if(s.charcodeat(i)>255) l+=2; else l++;
}
alert(l);
}(“hello world!”);
如何控制alert中的换行
alert(“hello\nworld”);
解释document.getelementbyid(“elementid”).style.fontsize=”1.5em”
设置id为elementid的元素的字体大小为1.5个相对单位
em为相对长度单位。相对于当前对象内文本的字体尺寸。
如当前对行内文本的字体尺寸未被人为设置,则相对于浏览器的默认字体尺寸。
1em=16px
按照格式 xxxx年xx月xx日xx时xx分xx秒动态显示时间要求不足10的补0
<script type=”text/javascript” language=”javascript”>
function tick(){
var d=new date();
var t=function(a){return a<10?”0″+a:a;}
clock.innerhtml=d.getfullyear()+”年”+t(d.getmonth()+1)+”月”+t(d.getdate())+”日”+t(d.gethours())+”时”
+t(d.getminutes())+”分”+t(d.getseconds())+”秒”;
window.settimeout(“tick()”,1000);
}
window.onload=tick;
</script>
<body>
<p id=”clock”></p>
</body>
编写一个方法去掉一个数组的重复元素
array.prototype.strip=function(){
if(this.length<2) return [this[0]]||[];
var arr=[];
for(var i=0;i<this.length;i++){
arr.push(this.splice(i–,1));//将本数组中第一个元素取出放入到数组arr中
for(var j=0;j<this.length;j++){
if(this[j]==arr[arr.length-1]){
this.splice(j–,1);//删除本数组中与数组arr中最后一个元素相同的元素
}
}
}
return arr;
}
var arr=[“abc”,85,”abc”,85,8,8,1,2,5,4,7,8];
alert(arr.strip());
说出3条以上ff和ie的脚本兼容问题
ie有children,ff没有;
ie有parentelement,ff没有;
ie有 innertext,outertext,outerhtml,ff没有;
ie有数据岛,ff没有;
ff有
htmlelement,htmlpelement,xmldocument,documentfragment,node,event,element 等等,ie没有;
ie跟ff创建httprequest实例的方法不一样
p中border、margin和padding的区别和用法
边框属性(border)用来设定一个元素的边线
外边距属性(margin)是用来设置一个元素所占空间的边缘到相邻元素之间的距离
内边距属性(padding)是用来设置元素内容到元素边界的距离
为array写一个indexof方法
array.prototype.indexof = function(e){
for(var i=0,j; j=this[i]; i++){
if(j==e){return i;}
}
return -1;
}
array.prototype.lastindexof = function(e){
for(var i=this.length-1,j; j=this[i]; i–){
if(j==e){return i;}
}
return -1;
}
var arr=[1,2,3,4,5];
alert(arr.indexof(5));
克隆
浅复制(影子克隆):只复制对象的基本类型,对象类型,仍属于原来的引用
深复制(深度克隆):不紧复制对象的基本类,同时也复制原对象中的对象.就是说完全是新对象产生的
object.prototype.clone = function(){
var objclone;
if( this.constructor == object )
objclone = new this.constructor();
else
objclone = new this.constructor(this.valueof());
for ( var key in this ){
if ( objclone[key] != this[key] ){
if ( typeof(this[key]) == ‘object’ ){
objclone[key] = this[key].clone();
}else{
objclone[key] = this[key];
}
}
}
objclone.tostring = this.tostring;
objclone.valueof = this.valueof;
return objclone;
}
兼容 ie 和 ff 的换行 css 推荐样式
word-wrap:break-word; overflow:hidden;
word-wrap是控制换行的。使用break-word时,是将强制换行。中文没有任何问题,英文语句也没问题。但是对于长串的英文,就不起作用。
word-break是控制是否断词的。
normal是默认情况,英文单词不被拆开。
break-all,是断开单词。在单词到边界时,下个字母自动到下一行。主要解决了长串英文的问题。
keep-all,是指chinese, japanese, and korean不断词。即只用此时,不用word-wrap,中文就不会换行了。(英文语句正常。)
手型cursor的兼容ie和ff写法
cursor:pointer
<img>元素的alt和title有什么异同?
alt作为图片的替代文字出现,title是图片的解释文字
图片存在
只有alt 图片的解释文字
只有title 图片的解释文字
两者都有图片的解释文字
两者都没有图片既没有替代文字,也没有解释文字
图片不存在
只有alt 图片既有替代文字,又有解释文字
只有title 图片没有替代文字,只有解释文字
两者都有图片既有替代文字,又有解释文字
两者都没有图片既没有替代文字,也没有解释文字
当然不同的浏览器处理方式也会不一样
border-color-left、marin-left、-moz-viewport改写成javascript格式
border-color-left:borderleftcolor
marin-left:marinleft
-moz-viewport:mozviewport
以上就是页面优化的方法的详细内容。
