前端开发学习指南(一):https://developer.aliyun.com/article/1413365
自打web开发早期开始,就流行一种产生唯一ID的方法。具体做法是把从1970年1月1日开始计算的微秒数加到你的静态ID后面,如下所示:
var myID = "static" + new Date().getTime();
这本来是相当万无一失的方法,因为即便两段这样的代码连续执行,在它们执行的间隙也会有几毫秒。可是现在新的浏览器带着新的Javascript引擎,伴随着一直在提升的主频。到现在,上面的代码产生相同的毫秒数的可能性会比产生间隙的可能性更大。
这会导致传统方法难以debug的bug。因为你的DOM是在运行中创建的,对页面源代码进行传统的测试无法确定多个重复ID的错误。Javascript和jQuery的错误处理机制会把第一个匹配的作为ID并忽略其他的重复ID。所以它甚至都不会抛出JS错误!
这样不行,唯一真正的方法是逐行设断点和log,但是如果断点的位置不对,你的毫秒数又不会冲突了!
好消息是有很多产生唯一ID的替代方法。学究气一点的说法是,计算机的随机数函数其实并不是真正随机的,因为它还是来源于系统时间,虽然这一点值得注意,但是随机数冲突的可能性是微乎其微的。
var myID = "static" + Math.round(Math.random() * 10000);
我个人更偏爱人工产生GUID方法。从技术角度说,GUID是根据你的硬件创建的,不过下面的Javascript函数做得相当棒。这是我从 stack overflow 的一个帖子 里偷来的,相当顺手的一个函数。
function S4() { return (((1+Math.random())*0x10000)|0).toString(16).substring(1); } function guid() { return (S4()+S4()+"-"+S4()+"-"+S4()+"-"+S4()+"-"+S4()+S4()+S4()); } var myID = "static" + guid();
检测特性,而不是检测浏览器类型
用户的浏览器是否支持地理信息?用户的浏览器是否支持web works?HTML5 视频?HTML5 音频?答案曾经是这样的:
if ($.browser.msie) { // 哦,是IE啊,那肯定不支持 }
但是世界在快速变化。最新版的IE几乎能算是现代浏览器了,但它依旧给前端开发带来痛苦。更早版本的IE基本上和它之前的版本一样烂,这就让偷懒的Javascript程序员习惯于检测 if (ie) 然后执行一些微软专用的破语法。现在IE9已经废弃了这些专用函数,那些原来的 if (ie) 老古董就反而会坏事了。
那么,如果能检测每个特性而不用检测(既不可靠又能伪装的)user-agent,你觉得咋样?
如果你的回答是 "那相当靠谱", 那你就说对了。
用 Modernizr 吧,这是行业梦幻级大师Paul Irish参与开发的一个Javascript库。该库集广泛应用、轻量级和海量文档三大优势于一身,实施无需动脑,实为居家旅行、杀人灭口必备。它会产生一个 Modernizr 对象,其中包含了它所有检测测试的结果,这样检测某个特性的支持与否就和下面的例子一样简单:
// 检测浏览器是否支持canvas的老办法 if (!!document.createElement('canvas').getContext) { ... } // 用 Modernizr 检测 if (Modernizr.canvas) { ... }
使用可读的毫秒数
毫秒数的一种方便的写法是写成可读的。对于初学者这很棒,但是大部分情况下其实只是一个噱头。
// 这是3秒,30秒还是300秒啊? var timeout = 30000; // 增加了额外的计算开销,但是读和修改会更容易 var timeout = 30 * 1000;
关于jQuery代码
像疯狗一样串接
jQuery最好的特性之一就是它的函数串接。你可能已经用过一点,也许把一些简单的调用一个接一个串起来...但是你是否曾经像头疯狗一样在DOM里上蹿下跳地遍历呢?还是花点时间来熟悉一下 .end() 函数。等你从起始选择器开始在DOM里上蹿下跳的时候,这个函数会很关键。
$(".quote") .hide() .find("a").text("Click here").bind("click",doStuff).end() .parent().removeClass().addClass("testimonial").draggable().end() .fadeIn("slow");
上例中,每次我们完成对某个DOM对象的操作,要反向遍历DOM返回我们引用的原始对象的时候,就需要使用 .end() 函数。然后我们就顺藤摸瓜扎回原来DOM里的位置了。
前端面试 用前端面试题库小程序 MST题宝库
使用 data-* 属性
你们当中那些已经写了很长时间Javascript(原生的,不是jQuery)代码的同学,很可能都熟悉各种属性吧。你们想办法设置它们,获取它们,或者滥用 rel 和 title ...
别说HTML5 或者 jQuery 没帮上忙哦。新的描述中允许在HTML元素中使用 data- 前缀来指明包含数据的属性,jQuery会把指定的字符串转换成正确的Javascript数据类型,这活干的非常漂亮。我们来创建一个带有某些数据属性的 DIV 。
<div id="test" data-is-bool="true" data-some-number="123"></div>
现在,即使我们的值被包装在引号里面,它们也不会被当做字符串处理:
typeof $("#test").data("isBool"); // boolean typeof $("#test").data("someNumber"); // number
特殊的大小写
要注意,要让这些代码片段正常工作,(HTML里的属性定义)必须使用小写字母,这很重要。不过如果你是一位很强的前端开发者,你还是会想用驼峰法来命名你的数据变量。正如在Javascript里很多地方出现的,前置的连接符意味着下一个字母会适用于驼峰法大写。不过,下面的例子里在HTML属性定义中使用驼峰法是 不行的 ,会让上面的Javascript代码返回 undefined。
不好使 :( <div id="test" data-isBool="true" data-someNumber="123"></div> 好使 :) <div id="test" data-is-bool="true" data-some-number="123"></div>
'.stop()' 停止协作和监听
把jQuery动画效果和鼠标事件绑定是基于web的现代用户交互方式中的关键部分,可是这方面即便某些最有名的网站也做得很蹩脚。这篇文章 提供了一个实现动画的直接例子并且演示了这些动画放在一起在视觉上会产生多么不和谐的效果。 好在这个问题可以利用一个函数前缀或在 $.animate 调用中加入一个参数来轻松解决。
在使用 $.animate 的时候, 可以在参数中加入 queue: false 来避免串接。诸如 $.fadeIn 或 $.slideDown 这样的动画快捷方式不接受 queue 设置,你必须用 $.stop 这个方法预先停止这些动画.。 在特定的场景下,需要某个动画直接停下,或跳转到变换的最终状态。推荐你先熟悉有关 clearQueue 和 jumpToEnd 这两个参数的相关 文档 ,因为老天在上,我没有其他办法帮你。
$("selector").stop(true,true).fadeOut(); $("selector").animate({ property: value }, { duration: 1000, queue: false }
优化你的选择器
jQuery 很高冷。它几乎无所不能,不过它目前还没法给你冲咖啡,我听说在2.0版的路线图里有才这个特性。你需要当心的一件事是别滥用它的 sizzleJS 选择器引擎的能力。想避免这种问题可以有两个策略:缓存选择器结果 以及 使用高效率的选择器。
缓存选择器结果
是每次你要修改一点东西的时候都先进行开销巨大的DOM查询,还是保存一份元素的索引?选择一目了然。
// before $(".quote a").bind("click", doStuff); // DOM查询 // now $(".quote a").addClass("quoteLink"); // DOM查询!! // later $(".quote a").fadeIn("slow"); // 又一次DOM查询!!!
忽略串接,这样做更好:
// before var $quoteLinks = $(".quote a"); // 只需一次DOM查询 $quoteLinks.bind("click", doStuff); // now $quoteLinks.addClass("quoteLink"); // later $quoteLinks.fadeIn("slow");
使用高效率的选择器
好了,jQuery/sizzleJS 可以轻松使用CSS3选择器,但是真正的开销是什么? 在这种场景下浏览器有可能会使用 document.querySelector(), 但是它也有可能分拆你的选择器字符串,然后手工去查询DOM。
// ID搜索是最快的查询方式,然后它获取孩子节点的列表,匹配其中class为'quotes'的元素
$("#quoteList").children(".quotes"); // 只在预先确定的bar元素下查找'foo'class $(".foo",bar);
前端面试 用前端面试题库小程序 MST题宝库
'for' 循环总是比 'each()' 循环快
不管未来几年在浏览器开发领域会发生什么,本地的 for 循环永远会比jQuery的 $.each() 循环快。 当你思考jQuery到底是什么(把本地JS函数包装起来的一个库)这种高大上问题的时候,你就会开始认识到本地原生Javascript代码永远会更快。用库还是用原生,这是一个运行速度和编程速度之间的权衡。
很重要的一点是,对那些可能每秒调用数百次的性能关键的函数,总是要使用 for 循环。例如:
- 鼠标移动
- 时间间隔
- 循环内部的循环
CSS
理解盒子模型是关键
"盒子模型"对于理解浏览器如何渲染页面是关键性决定性的因素。对其复杂性的全面理解有助于奇迹般地简化你的工作。盒子模型描述了对HTML元素的物理维度进行计算的方式。如果一个块元素具有固定宽度,比如说100px,那么应该如何确定它的 padding, border 和 margin 呢?
很多网站都有深入的描述,但咱们简单点说:在遵循标准的浏览器中,border和padding是被放在指定宽度之外的。最好是用图形来解释。比如下列代码:
.foo { width: 150px; height: 150px; padding: 25px; border: 25px solid; margin: 20px; }
你可能估计的情况(Quirks 模式)
padding 和 border 都是往里算的,结果保持高度和宽度都是150px。
你看到的情况(遵循标准模式)
可是,实际上出来的宽度和高度都是250px。 也就是150px + (2 * 25) + (2 * 25)。
如果你觉得这个结果很奇怪,那你不是一个人(呃,你是人,只是说还有其他人也会这么想)。 现在手头有个修复办法,需要引入一个CSS属性叫 box-sizing,这个属性对于 IE8 及以上版本 都适用。它允许你选择计算元素维度的确切方式,这样就能救你于危难之中。具体支持的参数因浏览器而异,另外需要用到浏览器厂商的前缀,具体细节请参阅 caniuse.com 。
/* 旧方法 (178 + 20 + 2 = 200) */ .foo { width: 178px; padding: 10px; border: 1px; } /* 更好的方法 */ .foo { width: 200px; padding: 10px; border: 1px; -webkit-box-sizing: border-box; -moz-box-sizing: border-box; box-sizing: border-box; }
虽然说你也总是可以对宽度进行心算,在各个像素数中减来减去(就像第一个方法做的那样),但在涉及到不同的宽度单位时(比如百分比或者EM),就没人能搞清楚到底该怎么做了。目前,除了把元素包在父元素中,以确保宽度和 padding/margin/borders 可以全部分开之外,也没有别的解决办法。
知道什么时候用float,什么时候用position
用table进行布局的时代过去了。现在要承认我们可以集中精力去理解float和position的工作原理。这里需要掌握一套特别的思维模型,我相信这件事最好是通过动手练习来进行。
用float从DOM中提取元素并强制它们靠到左边或右边,那是相当靠谱。它们已成为前端开发的后table布局时代的万金油,这可能是因为以前浏览器对于 display: inline 和 inline-block 的支持不力,还有对position的支持中冒出的 z-index bug。可现在就真的没有借口了。 inline-block 已经支持得很好了,简单的一点修正就能让它在 IE7 里应用。
谢天谢地,以前那些阻挠用CSS对元素进行绝对定位的争论都消亡了。理论上,定位属性可以让你在页面上以X和Y坐标放置元素,这种方式简单直接,Flash开发者们都应该很熟悉。
理解 Position
用CSS定位元素的时候,理解一个事实非常重要:定位的位置总是相对于离它最近的有定位属性的父元素而言的。人们刚开始用CSS的时候会有一个常见的误解,认为 position: absolute; 是相对页面的root元素定位的。 我觉得这种误解来源于某些情况下,元素没有任何父元素具备position样式 -- 在这种情况下,他们的结论是对的。这样向上遍历DOM树没有找到任何有定位样式的元素,就会定位到页面的根元素上。
那么,如果 position: absolute; 是把元素从他们所在的流中抽取出来,那你如何相对一个元素的父元素对它进行定位呢? 方法很直接。父元素需要定义 position: relative; 样式,然后所有的孩子元素就会按上、右、下、左的顺序依次摆放。利用这个知识,你会如何实现下面很直观的布局呢?
使用 float,你会需要把这些元素包在一个父元素中, 然后把.one float靠左,然后改动 .two 和 .three 的 float 和 margin 。最后你应该写出类似下面的东西:
前端面试 用前端面试题库小程序 MST题宝库
.parent { /* ghetto clearfix */ width: 310px; overflow: auto; } .one { width: 200px; height: 210px; float: left; } .two { width: 100px; height: 100px; float: right; margin-bottom: 10px; } .three { width: 100px; height: 100px; float: right; }
正如我们前面所说,使用 position 让我们可以用很明确的方式,按照 X 和 Y 坐标把元素显示在屏幕上。 上面用float的方式会把页面上的长文字隔开,下面的方法则可以确保所有元素处于正常位置,无论页面上有什么内容。
.parent { position: relative; width: 310px; height: 210px; } .one, .two, .three { position: absolute; } .one { top: 0; left: 0; width: 200px; height: 210px; } .two { top: 0; right: 0; width: 100px; height: 100px; } .three { bottom: 0; right: 0; width: 100px; height: 100px; }
如前文所述,有些 z-index 的问题需要考虑。虽然上面的例子可能显得有点过分,不过一旦你开始思考定位,它会打开一个各种可能性的新世界.
留空
如果我们在单行和多行CSS参数的格式之间变来变去,CSS里的留空也会不一样。我不打算对这个说太细。
合适的空白
/* 不好 */ .selector {display:none;background:#ff0000;color:#000000;} /* 好 -- 单行 */ .selector { display: none; background: #ff0000; color: #000000; } /* 好 -- 多行 */ .selector { display: none; background: #ff0000; color: #000000; }
大括号不换行
.selector { display: none; background: #ff0000; color: #000000; }
子元素缩进
这个用不用就见仁见智了,我个人只会在单行定义的CSS文档中用这种格式。
.selector { display: none; background: #ff0000; color: #000000; } .selector a { text-decoration: none; } .selector span { font-weight: bold;
组合并缩进浏览器厂商前缀属性
.selector { background: #FFF; border: 1px solid #000; color: #EAEAEA; -webkit-border-radius: 3px; -moz-border-radius: 3px; border-radius: 3px; }
CSS 速记格式
属性分组
把属性分组到一起是大大减少CSS文件大小的最有效方法。这里很重要的一点是要理解属性是如何排序的(顺时针 -- 上,右,下,左),以及如何进一步缩短它们(上和下,左和右)。
/* 逐个定义,太长了 */ padding-top: 1px; padding-right: 2px; padding-bottom: 1px; padding-left: 2px; /* 上,右,下,左,好很多 */ padding: 1px 2px 1px 2px; /* 上和下,左和右,最优 */ padding: 1px 2px;
从 0px 到英雄
给值为 0 的属性分配一个单位类型是多余的。一个元素是距离左边 0px 还是 0 elephants 根本不重要,只要知道它是贴着左边就行了。
/* 不好 */ padding: 0px 10px; /* 好 */ padding: 0 10px;
注释块
对于在一个样式表里维护多个样式区域的任务,给大段CSS加上注释是很好的办法。显然这和单行CSS风格配合使用效果更佳,不过这个效果在多行CSS风格里也不是完全没用。注释里用破折号、等号还是下划线起强调作用就见仁见智了,不过下面是我喜欢的方式:
/* === HORIZONTAL NAV === */ #horizNav { width: 100%; display: block; } #horizNav li { display: block; float: left; position: relative; } #horizNav li a { display: block; height: 30px; text-decoration: none; } #horizNav li ul { display: none; position: absolute; top: 30; left: 0; } /* === HOME PAGE - CAROUSEL === */ #carousel { width: 960px; height: 150px; position: relative; } #carousel img { display: none; } #carousel .buttons { position: absolute; right: 10px; bottom: 10px; }
前端面试 用前端面试题库小程序 MST题宝库
清除浮动
清除一个 <div> 过去意味着额外的DOM,因为这会涉及到增加一个额外的清除元素。更好的办法是给父元素设置明确的宽度('auto'并不是在所有浏览器和场景中有效)以及把overflow属性设为'auto'或者'hidden'。'hidden'显然兼容性更好,但在某些兼容IE的版本里'auto'的效果好一些。
HTML: <div class="parentElement"> <div class="childElement"> I'm floated left! </div> I'm normal text that wraps around the float </div> CSS: .parentElement { width: 100%; overflow: hidden; } .childElement { float: left; }
有本项目的贡献者提醒我注意最新的clearfix。 micro clear-fix 被认为相当稳定且跨浏览器兼容,足以列入最新的HTML5 boilerplate发布了。 我 强烈 建议你去看看。虽然我不是浏览器特定CSS和 :after 这种伪元素的狂热粉丝,不过这个micro clearfix的确更健壮。它还能避免顶层margin缩回的问题。
垂直和水平居中
水平居中元素实际上不是什么高精尖的科技,我敢肯定你们大部分人都熟悉下面的代码片段:
.class { width: 960px; margin: 0 auto; }
前端开发者们使用这种代码很长时间了,也没搞明白为什么这种方式对垂直居中不起作用。从我的理解说,很重要的一点是记住父元素一般会有个 height: auto; 样式, 也没有垂直居中元素所需的100%高度。而应用 position: absolute; 能有效地把元素转移到定位模式,然后被设为auto的margin会自动帮助它调整位置,达到居中效果。
.exactMiddle { width: 100px; height: 100px; position: absolute; top: 0; right: 0; bottom: 0; left: 0; margin: auto; }
这种方法的不足之处包括在 IE6 和 IE7 中缺乏支持,以及当浏览器被缩小到比居中对象还小时不出现滚动条。 在 这个网页 里列出了更多的方法(现在这个是第4个),不过现在这个目前是最优方法。
在一个元素里垂直居中文字也是很直接的。如果文字是单行的,例如一个水平导航元素,你可以设置 line-height 为该元素的物理高度。
#horizNav li { height: 32px; line-height: 32px; }
检测特性,而不是检测浏览器类型
在前面关于Javascript特性检测的讨论中,检测到浏览器是 任何版本 的 IE 然后就运用某些属性的做法越来越成问题了。铁人 Paul Irish 引领了使用 IE 版本检测 方法来解决这些问题的大潮,但是 Modernizr 从那时起拯救了我们。 Modernizr 在 root <html> 元素里放入一些class,描述某些特性是否得到支持. 然后前沿的样式就可以很容易从这些class级联出来或者删除掉。
.my_elem { -webkit-box-shadow: 0 1px 2px rgba(0,0,0,0.25); -moz-box-shadow: 0 1px 2px rgba(0,0,0,0.25); box-shadow: 0 1px 2px rgba(0,0,0,0.25); } /* 如果 box shadow 不支持, 就应用 borders 属性 */ .no-boxshadow .my_elem { border: 1px solid #666; border-bottom-width: 2px; }
!important 不要乱用
依赖于 !important 标签是个危险的现象。非用它不可的情况屈指可数,而且是特殊情况。这些情况大抵是需要覆盖另外一套样式表,而你没法或者没权限编辑它。另一个场景是对元素的样式硬编码以防止Javascript在线产生的样式有更大优先级。而实际情况是 !important 往往被用做偷懒的快捷方式,让某个样式优先于其他的样式,这样做将来会产生很多问题。
对 !important 标签的大部分使用是可以避免的,只要更好地理解CSS选择器优先级以及如何更准确地定位元素。选择器越具体,被接受为适用样式的可能性就越大。下面来自 vanseodesign 的例子展示了具体化起作用的情况。
p { font-size: 12px; } p.bio { font-size: 14px; }
关于样式优先级, 他们的文章 在解释继承性方面比我能写出来的文章都好,所以请给它点个赞吧。
进取性向下兼容
值得注意的是,这段是我的个人观点,只适用于特定情况。在依赖老版本浏览器的大型商业项目或企业级解决方案中,进取性向下兼容的立场将不容易被接受。
进取性向下兼容的意思是如果某个特定的(老版本)浏览器无法渲染某个特定效果,则应直接忽略它。CSS3 按钮就是一个好例子。诸如 border-radius, box-shadow, text-shadow 和 gradients 这些效果会在先进的浏览器里显示出来。对于版本稍微老一点的浏览器,可以用一个 .PNG 图片作为无伤大雅的补救办法,而所有解决办法中最优雅的应该是针对IE6提供一个PNG-Fix,或者用filter 参数来代替 gradients 和 shadows等属性。 不过,在这种情况下,进取性向下兼容方式会让你忽略老版本浏览器,而在其中展示一个平面的还过得去的对象。
简单地说,进取性向下兼容说白了就是:如果你的浏览器渲染不了渐变色或盒子阴影,那是你运气不好。
虽然这不是对所有情况都理想,这种方法能确保项目按时交付,且核心产品是可用的,而不需依赖于对浏览器的破解办法。
前端面试 用前端面试题库小程序 MST题宝库
CSS3及HTML5
这个话题我想我已经说的够多了。用 Modernizr 来检测特定的 HTML5 和 CSS3 特性是否可用。
@font-face的使用和滥用
在你考虑嵌入一套定制的字体之前,很重要的一点是你要查看 EULA 并检查是否允许web嵌入。 字体库厂商自然是不愿意让设计师和开发者有能力把字体库文件直接存放在服务器上,然后被熟练的终端用户拷贝走。某些厂商也禁止嵌入特定的文件类型,例如 .TTF 和 .OTF。
如果,经过慎重考虑,你相信想要的字体是可嵌入的,那就去看一下Font Squirrel的 @font-face 生成器。 它利用了 Fontspring的 防弹 @font-face 结构 并能自动生成所有需要的文件格式。
向下兼容
谢天谢地,浏览器对于它不支持的HTML5 和 CSS3 特性的处理已经达到了优雅的本色。加到 <input /> 标签的新类型例如 "email", "search" 等等在浏览器本地不支持的情况下一般会向下兼容为正常的 <input type="text" />。 类似的,不支持的CSS3 特性就不会出现,由高度和宽度媒体查询控制的响应式布局也不会被应用。
精巧的CSS3效果应该被应用为对使用现代浏览器的用户的一种奖励。
在下面的"资源"小节里包括了一些有助于让HTML5和CSS3功能在一批老版本浏览器中保持正常的库。