JavaScript清除历史的一个小问题

1.不要做任何假设

(JavaScript是个不靠谱的助手)

JavaScript最重要的特性之一,可能并不突兀,就是你必须停止做任何假设:

*不要假设JavaScript可用,你最好认为它很可能不可用,而不是直接依赖它。

*不要假设浏览器支持某些方法和属性,直到您测试过它们并确认它们可以使用。

*不要假设HTML代码有你想的那么正确,每次都检查,没有就什么都不做。

*使JavaScript函数独立于输入设备。

*请记住,其他脚本可能会影响您的JavaScript的功能,因此请确保您的脚本的范围尽可能安全。

在开始设计脚本之前,首先要考虑的是检查要为其编写脚本的HTML代码,看看哪些代码可以帮助您实现目标。

2.找出钩子和节点之间的关系

(HTML是脚本的基石)

在开始编写脚本之前,先看看要为其编写JavaScript的HTML。如果HTML是无组织的或未知的,你几乎不可能有一个好的脚本方案——很可能会出现以下情况:要么用JavaScript创建太多标签,要么应用程序过于依赖JavaScript。

HTML中有一些需要考虑的事情,就是钩子和节点的关系。

& lt1 & gt;。HTML挂钩

HTML的第一个也是最重要的钩子是ID,可以通过最快的DOM方法——getElementByID来访问ID。如果一个有效的HTML文档中所有的ID都是唯一的(IE中有一个关于名称和ID的bug,但是一些好的类库已经解决了这个问题),那么使用ID是安全可靠且易于测试的。

其他挂钩是HTML元素和CSS类。HTML元素可以通过getElementsByTagName方法访问,但是CSS类在大多数浏览器中不能通过原生DOM方法访问。但是,有许多外部类库提供了对CSS类名的访问(类似于getElementsByClassName)。

& lt2 & gt。HTML节点关系

HTML的另一个有趣之处是标签之间的关系。考虑以下问题:

*怎样才能最轻松的到达目标节点,并且DOM遍历最少?

*通过修改什么标签,可以访问尽可能多的子节点?

*给定元素可以使用什么属性或信息来到达另一个元素?

遍历DOM是资源密集型的,而且很慢,这就是为什么我们应该尝试使用浏览器中已经使用的技术来做这件事。

3.把遍历留给专家吧。

(CSS,更快地遍历DOM)

有趣的是,许多人似乎对关于DOM的脚本和用于遍历DOM的方法或属性(getelementsbytagname、next sibling、previous sibling、parent node等)感到困惑。有趣的是,我们已经通过另一种技术——CSS来做这些事情了。

CSS是一种使用CSS选择器访问目标元素并通过遍历DOM改变其视觉属性的技术。使用DOM的复杂JavaScript可以由CSS选择器代替:

Java代码var n = document。getelementbyid(' nav ');if(n){ var as = n . getelementsbytagname(' a ');if(as . length & gt;0){ for(var I = 0;作为[我];i++){ as[I]. style . color = ' # 369 ';as[I]. style . text decoration = ' none ';}}}/*下面的代码和上面的功能一样*/# nav a { color:# 369;文字-装饰:无;} var n = document . getelementbyid(' nav ');if(n){ var as = n . getelementsbytagname(' a ');if(as . length & gt;0){ for(var I = 0;作为[我];i++){ as[I]. style . color = ' # 369 ';as[I]. style . text decoration = ' none ';}}}/*下面的代码和上面的功能一样*/# nav a { color:# 369;文字-装饰:无;}

这是一项可以很好利用的强大技能。您可以通过动态添加类或将元素ID更改为DOM中的高级元素来实现这一点。如果使用DOM向文档主体添加CSS类,设计人员可以很容易地定义文档的静态和动态版本。

Java代码JavaScript:var dynamic class = ' js ';var b = document.bodyclassName = b.className?b . class name+' js ':" js ";CSS: /*静态版本*/# nav {...}/*动态版本*/body.js # nav {...} JavaScript:var dynamic class = ' js ';var b = document.bodyclassName = b.className?b . class name+' js ':" js ";CSS: /*静态版本*/# nav {...}/*动态版本*/body.js # nav {...}

4.了解浏览器和用户

(在现有使用模式上创建您需要的内容)

不引人注目的JavaScript的一个重要部分是理解浏览器如何工作(尤其是浏览器如何崩溃)以及用户期望什么。不管使用哪种浏览器,您都可以使用JavaScript轻松创建完全不同的界面。拖放界面、折叠区域、滚动条和滑块都可以用JavaScript创建,但这个问题不是简单的技术问题。你需要考虑以下问题:

*这个新界面可以独立于输入设备吗?如果没有,你能依靠什么?

*我创建的新界面是否遵循了浏览器或其他丰富界面的准则(可以通过鼠标在多级菜单中直接切换吗?还是需要用tab键?)

*我需要提供什么功能,但是这个功能依赖于JavaScript?

最后一个问题其实不是问题,因为如果有必要的话,可以使用DOM凭空创建HTML。“打印”链接就是一个例子,因为浏览器不提供非JavaScript函数来打印文档,所以需要使用DOM来创建这样的链接。同样,实现内容模块扩展和收缩的可点击标题栏也属于这种情况。键盘不能激活标题栏,但链接可以。所以为了创建一个可点击的标题栏,你需要使用JavaScript来添加链接,然后所有使用键盘的用户都可以收缩和扩展内容模块。

解决这类问题的一个极好的资源是设计模式库。至于知道浏览器中独立于输入设备的是什么,那就要看经验的积累了。首先需要了解的是事件处理机制。

理解事件

(事件处理会引起变化)

事件处理是迈向不引人注目的JavaScript的第二步。重点不是让所有东西都可以拖动、点击或内嵌,而是要理解事件处理是可以完全分离的。我们已经分离了HTML、CSS和JavaScript,但在事件处理的分离方面还没有走得很远。

事件处理程序将侦听文档中元素发生的变化。如果有一个事件,处理程序将找到一个奇妙的对象(通常是一个名为e的参数),它将告诉元素发生了什么以及可以用它做什么。

对于大多数事件处理来说,真正有趣的事情是,它不仅发生在您想要访问的元素上,还发生在DOM中更高级别的所有元素上(但并不是所有事件都是这样,除了焦点和模糊事件)。例如,使用此功能,您可以只向导航列表添加一个事件处理程序,并使用事件处理程序方法来获取真正触发事件的元素。这种技术称为事件委托,它有几个优点:

*你只需要检查一个元素是否存在,但不需要检查每一个元素。

*您可以动态添加或删除子节点,而无需删除相应的事件处理程序。

*您可以在不同的元素上响应同一事件。

另一件要记住的事情是,当事件传播到父元素时,您可以停止它,并且您可以覆盖HTML元素(如链接)的默认行为。然而,有时这不是一个好主意,因为浏览器给HTML元素这些行为是有原因的。例如,链接可能指向页面中的一个目标,不修改它们可以确保用户可以标记页面的当前脚本状态。

体谅他人

(名称空间、范围和架构)

您的代码几乎不是文档中唯一的脚本代码。因此,确保代码中没有可以被其他脚本覆盖的全局函数或全局变量尤为重要。有一些模式可以避免这个问题。最基本的一点是使用var关键字初始化所有变量。假设我们编写以下脚本:

Java代码varnav = document。getelementbyid(' nav ');function init(){//do stuff } function show(){//do stuff } function reset(){//do stuff } var nav = document . getelementbyid(' nav ');function init(){//do stuff } function show(){//do stuff } function reset(){//do stuff }

上面的代码包含一个名为nav的全局变量和三个分别名为init、show和reset的函数。这些函数可以访问变量nav,并且可以通过函数名相互访问:

Java代码varnav = document。getelementbyid(' nav ');函数init(){ show();if(nav . class name = = = ' show '){ reset();} // do stuff }函数show(){ var c = nav . class name;// do stuff }函数reset(){//do stuff } var nav = document . getelementbyid(' nav ');函数init(){ show();if(nav . class name = = = ' show '){ reset();} // do stuff }函数show(){ var c = nav . class name;// do stuff }函数reset(){ // do stuff }

通过将代码封装到对象中,可以避免全局编码,这样就可以将函数转换为对象中的方法,将全局变量转换为对象中的属性。你需要使用“名称+冒号”来定义方法和属性,你需要在每个属性或方法后面加一个逗号作为分隔符。

Java代码varmyscript = { nav:document . getelementbyid(' nav ')、init: function () {//dostuff}、show: function () {//dostuff}、reset:function(){ // do stuff } } var myScript = { nav:document . getelementbyid(' nav ')、init:function(){ // do stuff }、show:function(){ // do stuff }、reset:function(){//do stuff } }

所有的方法和属性都可以通过“类名+点运算符”从外部和内部访问。

Java代码varmyscript = { nav:document . getelementbyid(' nav '),init:function(){ myscript . show();if(myscript . nav . class name = = = ' show '){ myscript . reset();} // do stuff },show:function(){ var c = myscript . nav . class name;// do stuff },reset:function(){//do stuff } } var myScript = { nav:document . getelementbyid(' nav '),init:function(){ myScript . show();if(myscript . nav . class name = = = ' show '){ myscript . reset();} // do stuff },show:function(){ var c = myscript . nav . class name;// do stuff },reset:function(){ // do stuff } }

这种模式的缺点是,每次从一个方法访问其他方法或属性时,必须在前面加上对象的名称,对象中的所有内容都可以从外部访问。如果您只是希望文档中的其他脚本可以访问部分代码,可以考虑以下模块模式:

Java代码var myScript = function(){ //这些是私有方法和属性varnav = document。getelementbyid(' nav ');function init(){//do stuff } function show(){//do stuff } function reset(){//do stuff }//公共方法和属性使用对象语法包装在返回语句中:return {public: function () {},foo:' bar ' }();Var myScript = function(){ //这些是私有方法和属性Varnav = document。getElementByid(' nav ');Function init(){//do stuff } Function show(){//do stuff } Function reset(){//do stuff }/Public方法和属性使用对象语法包装在返回语句中,return {public: function () {},foo:' bar ' } }();

你可以像前面的代码一样访问返回的公共属性和方法,在这个例子中:myScript.public()和myScript.foo .但是这里有一点不舒服:当你想从外部访问一个公共方法或者从内部访问一个私有方法的时候,你还是要写一个很长的名字(对象的名字可以很长)。为了避免这种情况,您需要将它们定义为private,并在return语句中只返回一个别名:

Java代码var myScript = function(){ //这些是私有方法和属性varnav = document。getelementbyid(' nav ');函数init(){ // do stuff }函数show(){ // do stuff // do stuff }函数reset(){//do stuff } var foo = ' bar ';function public(){ } var myscript = function(){//这些是私有方法和属性var nav = document。getelementbyid(' nav ');函数init(){ // do stuff }函数show(){ // do stuff // do stuff }函数reset(){//do stuff } var foo = ' bar ';函数public(){ }

//只返回指向要访问的私有方法和属性的指针。

Java代码返回{public:public,foo:foo } }();return { public:public,foo:foo } }();

这确保了代码风格的一致性,并且您可以使用较短的别名来访问方法或属性。

如果您不想向外部公开任何方法或属性,可以将所有代码封装到一个匿名方法中,并在其定义结束后立即执行:

Java代码(function(){//这些都是私有的方法和属性var nav = document。getelementbyid(' nav ');函数init(){//do stuff show();//类名前缀}函数show () {//dostuff}函数reset () {//dostuff}})()这里不需要;(function(){ //这些都是私有方法和属性var nav = document . getelementbyid(' nav ');函数init(){//do stuff show();//类名前缀}函数show () {//dostuff}函数reset () {//dostuff}})()这里不需要;

这种模式非常适合只执行一次且不依赖其他函数的代码模块。

遵循上面的规则,你的代码可以更好的为用户服务,也可以让你的代码在机器上运行的更好,和其他开发者的代码相处的更好。然而,还有另一个群体需要考虑。

7.为接手的开发者考虑。

(使维护更容易)

让你的脚本真正不引人注目的最后一步是写完代码后仔细检查,并照顾好一旦脚本上线就要接手你代码的开发人员。考虑以下问题:

*所有变量和函数名是否合理易懂?

*代码是否组织得当?从头到尾都很顺利吗?

*所有的依赖都是显而易见的吗?

*那些可能引起混淆的地方有没有加注释?

最重要的是要认识到文档中的HTML和CSS代码比JavaScript更容易被更改(因为它们负责视觉效果)。所以不要在最终用户可以看到的脚本代码中包含任何类和ID,而是将它们分开,放在一个存储配置信息的对象中。

Java代码myscript = function(){ varconfig = { navigation id:' nav ',Visible Class:' show ' };var nav = document . getelementbyid(config . navigation id);函数init(){ show();if(nav . class name = = = config . visible class){ reset();};//做stuff };函数show(){ var c = nav . class name;//做stuff };函数reset(){//do stuff };}();myscript = function(){ var config = { navigation id:' nav ',visible class:' show ' };var nav = document . getelementbyid(config . navigation id);函数init(){ show();if(nav . class name = = = config . visible class){ reset();};//做stuff };函数show(){ var c = nav . class name;//做stuff };函数reset(){//do stuff };}();

通过这种方式,维护人员知道在哪里修改这些属性,而无需更改其他代码。