作者:kun10
发布时间:May 3, 2011
分类:JavaScript
什么是javascript里面的ArrayLike对象? 一切都是源于看YUI的Array部分,Y.Array()方法本身就能把对象转化为数组的方法,接受一个叫arraylike的参数。 其实arraylike我们经常会遇到,函数的arguments对象就是一个最常见的arraylike对象。 我们可以在firebug里面console.log一个函数的arguments对象,会发现他是一个对象,不是会发现他带有一个length属性。 但是我们却不能用普通的数组的方法来操作arguments对象 比如:
arguments.pop();//arguments.pop is not a function
很奇特吧?其实arraylike的对象还不只arguments对象一个 document.getElementsByTagName(), document.images, document.childNodes这几个货都是arraylike的对象。 既然这些对象是arraylike的,我们自然很渴望使用array的方法来操作他们。 方法很早就有人写了。
var newArray = Array.prototype.slice.call(arguments);
原理: Array.prototype.slice: The original slice method that is given to all arrays via the prototype chain. We can’t call it directly though, because when it runs internally, it looks at the this keyword, and calling it here would make this point to Array, not our arguments variable. Array.prototype.slice.call(): call() and apply() are prototype methods of the Function object, meaning that they can be called on every function in javascript. These allow you to change what the this variable points to inside a given function.
中文注解: Array.prototype.slice是Array原型上面的slice方法,因为Arraylike对象不是array,所以直接使用slice是无效的,因此我们要把我们的arguments对象作为this,传入slice方法中。如何传入slice方法,我们使用call方式来把arguments作为this传给slice做处理。call和apply是Function构造函数对象的原型方法,因此我们可以对任意的函数使用。this最终被函数处理成一个数组。
这在非IE 的情况下面好使,因为在IE下面domnodelist不被认为是对象,对于IE我们只能使用慢一点的方法了。
var newArray = [];
for(var i = 0;i< arguments.length;i++){
arr.push(arguments[i]);
}
return newArray;
此时newArray就是一个数组对象。 我们可以对他pop(),push()等等等等。。。 参考:http://nfriedly.com/techblog/2009/06/advanced-javascript-objects-arrays-and-array-like-objects/(这篇更靠谱点。。。) http://shifteleven.com/articles/2007/06/28/array-like-objects-in-javascript
作者:kun10
发布时间:January 28, 2011
分类:JavaScript
图解学习JavaScript第二部分翻译 原文分三篇,第一篇是说一些JavaScript的基础特性,第二篇是说JavaScript向对象的一些思路,第三篇是把JavaScript和Ruby做了对比。 我比较在意的第二部分。翻译一下试试。 原文地址:http://howtonode.org/object-graphs-2
图解学习之二 第一篇 幅用图描述了Javascript的语法,这篇我会讲解三种JavaScript用于对象创建的技巧,分别是
我希望本文能够帮助人们理解每一个技巧的优点和缺点。
一、传统JavaScript构造器 我们先来创建一个简单的包含原型的构造器,这是你在原生JavaScript里面能找到的和类最相近的东西。他很强大。 但是,他又与传统的类语言有区别。
如下:
function Rectangle(width, height) {
this.width = width;
this.height = height;
}
Rectangle.prototype.getArea = function getArea() {
return this.width * this.height;
};
Rectangle.prototype.getPerimeter = function getPerimeter() {
return 2 * (this.width + this.height);
};
Rectangle.prototype.toString = function toString() {
return this.constructor.name + " a=" + this.getArea() + " p=" + this.getPerimeter();
};
现在,我们再来创建一个新的类叫做Square,他继承于Rectangles。为此,Square的构造器的原型要继承父类的原型。 然后我们会演示如何重写getPerimeter方法。
function Square(side) {
this.width = side;
this.height = side;
}
Square.prototype.__proto__ = Rectangle.prototype;
Square.prototype.getPerimeter = function getPerimeter() {
return this.width * 4;
};
最后的调用也很简单,只要用new来创建一个实例。
var rect = new Rectangle(6, 4);
var sqr = new Square(5);
console.log(rect.toString())
console.log(sqr.toString())
传统构造器方式注意点:
对于上面的例子,注意rect实例和Square.prototype并不一样,虽然他们都是继承于Rectangle原型的简单对象,JavaScript的对象是一串链式的调用。 唯一特殊的对象是function,它们获取参数,并且掌握着内部执行的代码和作用域。
二、纯原型链式 我们依然来实现上面的例子,但我们这次不使用构造函数,这次我们只用原型链式继承。
我们先来定义一个Ractangle的原型作为我们所有对象的基础。
var Rectangle = {
name: "Rectangle",
getArea: function getArea() {
return this.width * this.height;
},
getPerimeter: function getPerimeter() {
return 2 * (this.width + this.height);
},
toString: function toString() {
return this.name + " a=" + this.getArea() + " p=" + this.getPerimeter();
}
};
现在我们再来定义一个子对象,它叫Square,并且重写了一些方法。
var Square = {
name: "Square",
getArea: function getArea() {
return this.width * this.width;
},
getPerimeter: function getPerimeter() {
return this.width * 4;
},
};
Square.__proto__ = Rectangle;
然后我们就来看看创建具体的对象,我们在原型对象的基础上创建新的对象,并手工的修改它们的本地属性。
var rect = Object.create(Rectangle);
rect.width = 6;
rect.height = 4;
var square = Object.create(Square);
square.width = 5;
console.log(rect.toString());
console.log(square.toString());
输出:
Rectangle a=24 p=20 Square a=25 p=20
继续看图:
它并不如构造器+原型的方式来的强大,但是它直接易懂,如果你熟悉一个原型链式继承的语言,你会觉得很熟悉。
三、对象工厂 这是我很喜欢的方式————用一个工厂函数来创建对象的方式。它的不同在于,我只用简单的调用一个函数,它每次返回一个新的对象,而不是用一个原型对象供所有构造函数共享。
下面是一个很简单的MVC系统,controller函数接受表示model和view的两个参数,并输出一个新的controller对象。 这个过程中所有的状态都存贮在作用域的闭包里面 。
function Controller(model, view) {
view.update(model.value);
return {
up: function onUp(evt) {
model.value++;
view.update(model.value);
},
down: function onDown(evt) {
model.value--;
view.update(model.value);
},
save: function onSave(evt) {
model.save();
view.close();
}
};
}
使用时只要简单的代入参数运行一下这个function。注意,现在我们不用先把工厂函数绑定给某个对象就可以直接传递函数给事件handlers(setTimeout)。 因为工厂函数没有在内部使用this,没有必要再去混淆this的值。
var on = Controller(
// Inline a mock model
{
value: 5,
save: function save() {
console.log("Saving value " + this.value + " somewhere");
}
},
// Inline a mock view
{
update: function update(newValue) {
console.log("View now has " + newValue);
},
close: function close() {
console.log("Now hiding view");
}
}
);
setTimeout(on.up, 100);
setTimeout(on.down, 200);
setTimeout(on.save, 300);
//输出
View now has 5
View now has 6
View now has 5
Saving value 5 somewhere
Now hiding view
上图:
此图对应了上面的调用过程,注意,我们可以通过工厂函数的隐藏作用域获取到两个匿名对象,也就是说,我们可以从工厂函数的闭包里面获取到model和view。
作者:kun10
发布时间:March 30, 2010
分类:JavaScript
----------以上是ppk在yahoo演说js events的视频-------------
----以下是本人利用自己的双耳听到的,用自己的脑袋理解和翻译出来的,可以作为参考,以下的“我” 表示PPK,如若误解,深表荣幸----
今天我会说一些关于JavaScript事件的事情,我很希望自己能启发大家一些什么,不过大伙都是高手,不管怎么,我还是要说说JavaScript事件。 大约一年前我打算对JavaScript的事件做一些深入的研究,并且我做了一张兼容性的表,大家可以通过下面这里来访问
http://quirksmode.org/dom/events
那么今天我会和大家谈论4件事情:
首先 会是key事件(按键事件),因为在这里存在很多的困惑,很多是关于他们如何工作的困惑。
然后 我会谈谈change事件,他是我很喜欢的一个事件,不幸的是,它的工作情况有些乱。
第三 ,我会谈谈委派focus事件,当然可能你已经知道了。委派的事件通常只会触发在鼠标或者是键盘事件上,但是我找到了一种办法使它也能够使用在focus上面。
最后 我会说说手机上面的事件,相当诡异的东西。。。
那么首先就来看看键盘事件吧,键盘事件有三个:keydown,keypress和keyup。大多数的人都认为自己很清楚他们在会何时触发,而我在这里要告诉你,其实未必就如你所想的那样。 我们先来看看定义,、 keydown事件:当用户按下键盘上面的一个键时会触发,用户一直按着这个键他就会持续触发。 keypress事件:有一点点的不同,用户按着一个字符键(原文是character key)才触发,就是说用户按了一个能在屏幕上输出字符的按键keypress事件才会触发,比如我们往一个textarea里面输入字符。 keyup事件:很简单,当我们释放一个按键时候会触发。 举一个例子,这样你能更明白些 比方说我按下了一个字母“o”键或是字母“i”键,那么keydown和keypress就都会触发,而当我按下的是一个特殊的按键比如是shift键,那么kyedown会触发,而keypress不会。 keydown和keypress的这种区别最初是由微软提出的,所有的ie都支持这种区分方式,一年前(08年吧)推出的safari3.1也适用这样的方式,不过这只是说明了keydown和keypress存在方式的不同。相比较,firefox和opera会同时触发多个事件,因为此时事件既是一个keydown事件又是一个keypress事件,所以我们应该会同时触发多个事件。这样很好,但是这样却无法解释为什么要同时存在两种事件、而不是一种(keydown)。所以其实我还是很喜欢微软的这种区分的。 好吧,我们再来重温一次概念,keydown会在任何键按下时触发,keypress只是在字符键按下时触发,以上是在ie和safari下的情况,另外,我目前没有google chrome,不过他和safari很相近,所以这个应该也适用于chrome浏览器。
OK,让我们来看一些实际问题,通常我们都会写一段脚本来检测用户按过哪个按钮,然后利用这样的结果做一些有趣的交互应用。
键盘事件拥有两个属性,keyCode和CharCode,他们之间有一些不一样之处。keyCode表示用户按下键的实际的编码,而charCode是指用户按下字符的编码。 因此,当我按下“a键”,我会得到keyCode 65和字母a的charCode 97(注意是小写的字母哦),而当我按下shift键,我会得到keyCode 16,而我不会得到任何的charCode,因为按shift并没输入任何的字符。就这样,很简单吧~~~ 我们现在得到两个属性了,两个属性各自包含了各种的信息,这真是好事~~~
但是接下来我们又遇到问题了,很复杂,无法解释但它就是这样的问题。keyCode属性。在keyDown事件里面,事件包含了keyCode – 用户按下的按键的物理编码。在Onkeypress里,keyCode包含了字符编码,即表示字符的ASCII码。这样的形式适用于所有的浏览器 – 除了火狐 ,它在onkeypress的时候keyCode返回数字0, 别问我为什么,它就这样。然后,火狐在Onkeydown中charCode返回数字0,在 onkeypress里,由charCode返回字符ASCII编码。而这个,只会在Firefox和Safari工作,因为他们是唯一支持charCode的浏览器。(这一段翻译感谢信息学院的AN同学的纠正)
让我们更进一步看点更实际的东西,如果你想获取用户实际敲击的按钮,用keyDown事件来获取事件对象,并获取keyCode ,这在所有浏览器都行的通。另一方面,如果你想获取用户输入的字符,那么就使用keypress来获取,然后获取charCode(火狐和safari)或是keyCode(其他浏览器) 。
然后,我想有时候你也会有阻止某个按钮默认行为的想法,很常见的是方向键 设想你有一个适合于键盘的拖拽菜单,你希望用户用方向键来实现一个拖拽的操作,因此你就要取消方向键的默认行为(使页面向上或是向下滚动),那么一般来说你就会在keyDown事件里面做这件事,因为如我所说的那样----keyDown会在一个按钮被按下时候触发,而keyPress只是在字符键按下时触发,但是很不幸,这样子在opera行不通(当然我得承认我没有测试最新版的opera,也许现在已经修复了这个问题?待测试)。因此,我今天不会讨论在opera下面阻止方向键的方法,很坦诚的说,我还没有一个答案。
这就是键盘事件的一些总结,不会很麻烦吧?我说了几种键盘事件,但是你得注意keydown和keypress的差别。
好了好了,来说说change事件吧。照理论说,change事件应该是很好的一个事件,因为他应该是在表单有变化的时候触发。比如,你想在表单提交时检测一些用户操作表单的事情,比如是检测和验证用户在各个域的输入。当然也有很多时候我们会动态创建一个表单或是表单域或是选框--这样的表单会和用户之间产生很复杂的交互,这时候我们就要知道用户什么时候对表单作了修改,从理论上说,change事件是最合适不过的了。
但是通常,我们总是会被迫去使用focus和blur事件或者是click事件去做检测表单变化的事,而不是用change事件。这是因为change事件不总是能很正常的运行着。我们分三种情况来讨论 1、文本框的变化事件(text field) 2、选择框的变化事件(select box) 3、复选框和单选框的变化事件
从文本框开始说吧,假设用户把焦点移动到了文本框上面(或用鼠标或用键盘的方式),之后又移去了焦点,比如说他进入了下一个文本框的输入。在这样的情况下,change事件不触发,因为没有什么东西改变了---注意,第一个文本框的值并没有改变。但是我们对上述事情稍稍做一些变化,如果我们说,用户点击了文本框,并输入了一些文本,然后再撤去焦点,这就触发了change事件,很好,他在所有浏览器中都如此工作。
然而,当我们使用selectbox的时候问题就来了,选择框事件是你可以想到的最古老的浏览器事件,我想大约在95、96年我们在浏览器中就有了selectbox了,他可能列出了漂亮的链接,并节省了页面的空间。我们已经使用选择框有很长一段时间了,你也可以用鼠标去做一个选择。首先你点击选择框,它便列出了选项,当你再次单击里面的一个选项时,就触发了change事件。因为浏览器知道:“ok,用户已经改变了选择框里面的值了”。这也是用于所有的浏览器。
但是,用户还可以用键盘来操作改变选择框(select)的值,这就为我们使用change事件带来了麻烦。首先用户会把焦点移动到selectBox上面,然后利用方向键来选择他需要的选项。这还不是什么问题,但是这种情况,在IE和opera下面,每当用户按一次方向键(切换选项),change事件就会触发一次。这明显是很大的一个问题,如果一个选项表有20个选项,那么用户从第一个选项移到最后一个就会触发19次change事件,这可以说是ie和opera很严重的一个bug。 而在firefox和safari里面,他们所做的工作就是,允许用户在选项之间用上下键来选择,然后会在用户blur这个选择框的时候触发change事件,通俗的说,你告诉浏览器,我已经选择好了,浏览器你可以用change事件看看我是不是做了改变----像firefox和safari这样的工作方式很好很正确。不过,在IE下面做一些类似于选框导航的工作,当用户用键盘来操作,使用change事件就很容易产生问题。这就是我们对于change事件的首个大问题。
不幸的事继续发生,当我们在复选和单选框上面使用change事件,情况会更糟,我以一个复选框为例,基本上他和单选的工作方式一样。当用户点击了复选框(我不仅仅指用鼠标点选) ,一个点选操作可以通过鼠标或是键盘来达到。总之,这样的一个点击使得复选框的值发生了变化---true or false。按常理这时候change就发生了,因为用户确确实实的改变了东西,这样的一件事也的的确确会在firefox,safari,opera里面发生,但是IE不会 。 IE到底怎么了?你点击了复选框而什么也没有发生。接下来呢,你用鼠标或是键盘把焦点移出这个复选框移到别的复选框上面,原来的这个复选框的blur事件就触发了,同时change事件随之触发。这样的情况对于一些整理表格的操作实现来说是很糟糕的,比如通过复选框来开启表单的隐藏选项,在IE下面你只能在blur一个复选框的时候开启隐藏选项,这样对于用户来说是很困惑的(因为会给用户带来一种滞后的感觉)。这也是IE下面一个很严重的bug。
你可以看到我在ppt上面给W3C也划了一个叉号,因为据我所知w3c的事件标准只是规定了blur,根据标准,一个change事件应该在一个表单域改变并被移去焦点时发生。现在我们可以总结一下,change在文本域里面工作的很不错,当我们用键盘操作select框是它很有意义,而对于单选框和复选框或者是你用鼠标操作select框change事件使用的意义就不太大。(我作为翻译认为这里ppk出现了口误,把鼠标和键盘操作selectbox的情况说反了,大家要看了欢迎纠正,另外ppk貌似也没有提到用鼠标scroll方式操作select框的值的方式)
这就是我们目前遇到的一个问题,而我目前还没有找到一个很好的解决方式。幸运的是,我们可以用click事件或是focus事件或是blur事件做一些弥补,虽然这不会花费太多的精力,但是我还是期望change事件能变得真正的实用。这样我们可以比较统一的用一个事件去侦测一个表单里面的变化并做一些有意思的事。目前这还是我们能期待的。
进入我们今天的第三个话题吧,事件委派(event delegation),我非常确信你(指yahoo的同学们)应该已经知道什么是事件委派,不过其他的人却不知道,我简单的介绍一下。基本来说,事件委派是减少定义事件处理的一种方式,我们用下拉菜单为例。用传统的方式,我们可能要对这个下拉菜单添加很多的事件处理函数,当你的鼠标停留在父级菜单上面,1级子菜单弹出,我们在移动鼠标到1级菜单上面,二级菜单又会出来。而当我们使用事件委派时情况就有所不同,事件委派利用了事件的冒泡,当用户移动鼠标到一个链接(link)上面,触发了链接的mouseover事件,同时通过冒泡mouseover事件冒泡到了链接的上一级元素,直至冒泡到整个文档上面,这个过程会触发链接(link)上级任何一个含有mouseover事件的元素的mouseover事件,比如会冒泡到li和ul的mouseover事件,那我们为什么不把处理事件的函数放在链接的上层呢?这样可以节省众多链接的事件处理函数的绑定。
曾经我的一个客户抱怨他的站点有点慢,尤其是在IE下面,于是我看了源代码,我发现他的下拉菜单有六七十甚至更多的链接,每一个链接绑定了自己的mouseover、mousedown、mouseout事件,这就使得页面变得缓慢了。 因为每一个浏览器都会为单独的事件处理开辟一个单独的内存,更何况是IE(拥有内存泄露的美名)。于是我告诉我的客户事件委派,然后。。。。(网页快起来了)
那么首先让我们来建立一个下拉菜单
<ul>
<li><a href=”#”>Multimedialize</a>
<ul>
<li><a href=”#”>Sound</a></li>
<li><a href=”#”>Java applets</a></li>
</ul></li>
<li><a href=”#”>Ajaxify</a>
<ul>
<li><a href=”#”>Web 2.0</a></li>
<li><a href=”#”>Web 3.0</a></li>
<li><a href=”#”>Web 4.0b</a></li>
</ul></li>
</ul>
我们首先把我们的mouseover和mouseout事件处理函数绑定到ul上面,这在所有浏览器中都行的通。
var dropdown = {
init: function (dropdown) {
dropdown.onmouseover = this.mouseOver;
dropdown.onmouseout = this.mouseOut;
}
}
不过如果一个用户不用鼠标,而是想用键盘操作打开这个下拉菜单,我们又该怎么做呢?这个问题有个我们带来了一个重要的主题,设备的独立性(让网页支持多种设备的操作)。这也是我想向大家解说的,我们做的东西要兼容各种设备。 基本的规则是,所有带有“mouse”在名字里面的事件,只是在你真正使用鼠标的时候工作。 听起来很简单吧,不过这总会被很多人忽视。因此,我们需要用户使用键盘的时候能告诉我们用户何时是鼠标移入何时又是鼠标移出的事件,那么目前看最合适不过的就是focus和blur事件。因此,如果事件通过鼠标mouseover或是键盘focus冒泡到上面的元素,冒泡到达ul时我们就执行目标函数展开菜单。当鼠标mouseout或者是键盘blur冒泡上去时,我们就执行关闭菜单的函数。但是问题在于,这不可行,因为focus和blur事件不会冒泡。
这就带给我们另一个问题,什么事件支持冒泡?什么事件又不支持冒泡? 我们先把事件分为两类:一类是设备事件(鼠标或是键盘按键事件),另一类是界面事件。
鼠标和键盘事件是在使用输入设备的时候触发的,我还在做一个更好的定义,不过基本是这个意思,基本上用户所作的就是点击鼠标按键、点击键盘按键、移动鼠标或其他的一些事,总之是和一个具体的外接设备相关的,这些事件的例子如,mouseout,mouseover,click,keydown,keypress,顺便说一声click是唯一既迟滞鼠标触发又支持键盘触发的事件:你用鼠标点击会触发click,用键盘tab移动焦点到目标然后回车也会触发click。click是很少的能够工作于多种设备下的事件。因此,一个click事件是很安全的。其他的一些设备事件总是只支持某一个设备,最后要说的,就是以上提及的这些事件支持冒泡,直到document级。
界面事件,在一个常规事件触发时执行,而不关注它是如何被触发的(翻译的有点生涩,欢迎提意见) 界面事件的一个比较好的例子是,表单的submit事件,当一个表单被提交他就会触发,而表单submit事件并不关心用户是用什么方式来提交表单的,可以使用户用鼠标点击了提交按钮提交表单,可以是用户输入文本按了回车提交表单,只要是提交了表单,submit事件就会触发。这类事件还有很多,又如onload,onupload,change,focus,blur,对了这里有我们苦苦追寻的focus和blur事件。因为我们希望我们的下拉菜单能够兼容多种设备。
大体来说,界面事件不支持冒泡, 对我们来说这真是一个不好的消息,因为如果我们想让键盘用户也能用我们的下拉菜单,我们就要用到focus和blur。但是focus和blur并不能冒泡上ul元素,我们就只能老老实实的把事件处理绑定在每一个li上面了。当然还有一个不算好消息的好消息,我们可以在事件捕获中得到focus和blur,事件捕获的工作是和事件冒泡相反的,但是它在IE下不被支持。。。不过我们一会儿会讨论IE。
我们说事件冒泡,我们点击了a元素,a的click执行,然后事件冒泡到了li,之后是ul,之后是document。事件捕获则是,在我们点击了a之后,事件从document一级级的下来,经过ul、li、然后才到达a并终止捕获。你会觉得冒泡和捕获这两个东西用哪个都差不多,不过当我向dean Edwards问了这个问题后,他说:“不不不,你应该在大多数情况下去使用事件捕获”。
当你要捕获一个focus事件,被绑定在目标元素的祖先元素上的事件就会被执行,这个可以在所有浏览器下使用,当然除了IE,因为它不支持事件捕获。因此基本来说,如果我们把焦点移上a元素上并同时使用事件冒泡,只有a链接的unfocus事件会被触发。但是我们反过来使用事件捕获,那么这个unfocus事件就会顺着document->ul->li的顺序执行下去。
为什么事件捕获和事件冒泡会有差别?我也说不出。这只是我测试出来的,我开始在opera里面测试,开始我以为他只是opera特有的,但是我在firefox下面也测试,哦?火狐也有这个特性,然后又测试了safari,呵呵,safari也有同样的表现,最后,我发现它几乎是一个跨浏览器的特性。这样让我觉得事件冒泡和事件捕获具有同样的作用,因为在我之前的感觉中,事件是否能被捕获并不最重要,但是能否冒泡就不一样了(能否被冒泡意义重大)。不管怎么说,目前的这种状况(应该是说冒泡和捕获的混乱的支持情况)意义不是很大,但他确实能解决很实际的问题,因为现在我们可以对focus和blur进行事件委派了。
我们要做的很简单,我们给根元素添加上addEventListener,并给这个函数的最后一个参数设为true,使其工作在捕获阶段。
var dropdown = {
init: function (dropdown) {
dropdown.onmouseover = this.mouseOver;
dropdown.onmouseout = this.mouseOut;
dropdown.addEventListener('focus',this.mouseOver,true);
dropdown.addEventListener('blur',this.mouseOut,true);
}
}
这样,一旦下拉菜单有focus事件发生,那么ul上面的事件捕获就会触发,绑定在上面的this.mouseOver函数就会被执行。别问why,他就是这样工作的。
当然,因为IE不支持事件捕获,我们还要添加一点点东西,做一些修改,IE虽不支持事件捕获但是他有自己的解决方案,这是IE所特有的。
var dropdown = {
init: function (dropdown) {
dropdown.onmouseover = this.mouseOver;
dropdown.onmouseout = this.mouseOut;
if (dropdown.addEventListener) {
dropdown.addEventListener('focus',this.mouseOver,true);
dropdown.addEventListener('blur',this.mouseOut,true);
}
dropdown.onfocusin = this.mouseOver;
dropdown.onfocusout = this.mouseOut;
}
IE带有两个类似于focus和blur的事件,但唯一的区别就在于他们支持冒泡,他们是focusin和focusout,我们给原来的代码添加上两行,dropdown.onfocusin, and dropdown.onfocusout。这样我们的事件委派就工作在了所有的浏览器中。
顺便说一声,YUI团队 已经把这种特性纳入了他们的库 之中,不过当你使用其他的一些库的时候,这样的技巧你也可以记一下以便必要时候用得到,这样的技巧很好,但是IE并不是总能提供类似于focusin和focusout这样的东西,所以那种时候你可能会抓狂。 ---------------------------------------------------------- 休息线 ---------------------------------------------------------- ......先翻译这三段吧,第四部分PPK讲的是手机端的js事件,看完之后感觉手机端的事件是相当的混乱,目前本人对此研究也不深入,待更明白一点的时候再做翻译吧。。。,上面翻译的也不是很到位,各位千万要提出意见,这里可以给出ppk的原文 ,欢迎对照指点。
另附:PPK的一些关于事件的文章
作者:kun10
发布时间:March 14, 2010
分类:牛人之整理
JavaScript 可算是世界上最流行的编程语言,它曾被 Web 开发设计师贴上噩梦的标签,虽然真正的噩梦其实是 DOM API,这个被大量的开发与设计师随手拈来增强他们的 Web 前端的脚本语言,如今越来越被重视,虽则如此,JavaScript 仍然拥有很多让人费解的东西。
1. 它以 Java 命名,但并不是 Java 它最初叫 Mocha, 接着改名为 LiveScript,最后才确定命名为 JavaScript,根据历史记录,Java 的命名与 Netscape 和 Sun 之间的合作有关,作为交换条件,Netscape 在他们备受欢迎的浏览器中创建了 Java 运行时。值得一提的是,这个名字的出台几近一个玩笑,要知道,LiveScript 和 Java 在客户端脚本方面存在敌对关系。
不管怎么说,人们后来不得不一再澄清的一件事就是,JavaScript 和 Java 毫无关系。
2. Null 是个对象? 看看这段代码,它返回的是 object。
console.log(typeof null); // object
这实在令人费解,假如 null 表示空值,它怎么可以是对象?简单说,它是 JavaScript 最初版本的错误,这个错误甚至被微软的 JScript 直接借用。
3. NaN !== NaN NaN,表示一个非数字的值,然而问题是,NaN不等于任何东西,甚至不等于它自己。
console.log(NaN === NaN); // false
这显然不对,事实上,如果要判断一个值确实是 NaN,你需要用 isNaN() 函数。
4. 全局变量 对全局变量的依赖一直被视为 JavaScript 最坏的部分(ECMA 的 JavaScript 5 已经去掉了全局变量,请参阅 ECMA 推出 JavaScript 5 - 译者注)。对简单的页面,这无所谓,但复杂的页面,如果包含大量 JavaScript 脚本,你很难知道某个全局变量是在哪里声明的,如果几个全局变量不小心重名,就会引发错误。
“The problem with JavaScript isn’t just that it allows them (global variables), it requires them.” – Crockford 5. 那些统统被探测为 Mozilla User-Agent 的浏览器 必须承认,事实上,这不是 JavaScript 的错,是各个浏览器有意为之。比如,以下是用 JavaScript 探测 Safari 时得到的结果:
console.log(navigator.userAgent);
// Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_5_8; en-us) AppleWebKit/531.21.8 (KHTML, like Gecko) Version/4.0.4 Safari/531.21.10
是否注意到其中的第一个单词 Mozilla/5.0,为什么 Safari 会被探测为 Mozilla,尽管 Safari 后来已经纠正这一问题,但仍然不能解释为什么它们要这样误导开发者。事实上,你会发现,绝大多数浏览器把他们的 User Agent 设置为 Mozilla,答案要回到10年前,这更多是一种策略。
User Agent 是一段用来标识当前浏览器身份的字符串,世界上第一个浏览器 Mosaic, 曾这样标志自己:
Mosaic/0.9 // browser name / version number
这很合理,因此当 Netscape 出来的时候,它保留了 Mosaic 这个传统,还在后面添加了一个加密方式部分。
Mozilla/2.02 [en] (Win95; I) // browser name / version / encryption
到目前为止,一切安好,直到 IE3 发布,当 IE3 发布的时候,Netscape 正如日中天,那时,很多服务器和程序已经部署了客户端探测机制,以便认出 Netscape,虽然现在看来,这很值得争议,但当时并没什么。当 IE 初次推出它们的 User Agent 标志的时候,是这个样子:
MSIE/3.0 (Win95; U)
这让 IE 很被动,因为 Netscape 已经能被很多服务器识别,因此,开发者们干脆希望 IE 被误认为 Mozilla,然后,再单独加一个 IE 的标签。
Mozilla/2.0 (compatible; MSIE 3.0; Windows 95)
如今,几乎所有浏览器都步 IE 后尘,将自己标识为 Mozilla,这大概是一种连锁反应。
6. 不一致的函数范围 参看以下代码:
// Create a function that will call a function with the name equal to parameter fn.
function foo(fn) {
if (typeof fn === "function") {
fn();
}
}
// Create an object with a property and a method.
var bar = {
barbar : "Hello, World!",
method : function() {
alert(this.barbar);
}
};
bar.method(); // Alerts Hello, World!
foo(bar.method); // If we call the foo function add pass the "bar.method" method, it somehow alerts "undefined."
foo(function() { bar.method(); }); // alerts Hello, World, after
foo(bar.method) 返回结果不同原因是,method 函数是被当作 windows 对象,而不是 bar 下的对象调用的。要解决这个问题,我们必须从传递的匿名函数中调用 bar.method() 。
7. 位操作符 JavaScript 和 Java 有不少共同之处,如位操作。
- and | - or ^ - xor ~ - not - signed right shift ??? - unsigned right shift - left shift
看看第一个 操作符,使用 应该更有效,因为 JavaScript 和 Java 不一样,JavaScript 没有整数,需要来回转换,因此,转换操作花的时间更长。
8. 太多的空值类型 诸如 null, false, undefined 一类的值几乎表示同样的意思,它们之间的不同又让人很迷惑。
!!(0); // false
!!(false); // false
!!(''); // false
!!(null); // false
!!(undefined); // false
!!(NaN); // false
9. 算术问题 虽然 JavaScript 包含很多算术操作,但你不妨运行一下下面的算式,".2+.4" 应该等于 ".6" 是不是,然而返回的确是 "0.6000000000000001"。JavaScript 在小数计算访问存在一些小问题。
console.log(.2 + .4); // 0.6000000000000001
为什么会这样?简单说,因为 JavaScript 使用 IEEE 标准进行二进制浮点运算,不过,对整数计算是没问题的。
10. 莫名其妙的代码错误 看看以下两段代码:
// braces on the right
return {
foo : bar
};
// braces on their own line
return
{
foo : bar
};
它们应该是一样的,只是 { 位置不同而已,是吧。然而我们再看下面的代码:
如果我们把其中的
var foo = function() {
return {
a : 'b'
};
}();
alert(foo.a); // b
换成
return
{
a : 'b'
};
就会引发错误,这是因为 JavaScript 有一个功能,会纠正它认为错误的代码书写,它会自作聪明地在 return 这个词后面插入一个 ";" ,错误因此而生。
return; // JS incorrectly adds this semicolon.
{
a : 'b'; // It'll add a semicolon here as well, because it doesn't realize that this is an object.
};
本文来源:http://net.tutsplus.com/tutorials/javascript-ajax/top-10-things-that-javascript-got-wrong/
作者:kun10
发布时间:March 9, 2010
分类:牛人之整理
看玉伯的blog 有更新,才看了第一句就引发了疑问了~~~ 于是找了下面的资料 Script中的Defer属性
如果你是一个对系统性能比较关心和在意的人,我想你应该会对Script脚本中的defer属性感兴趣的。 script中的defer属性默认情况下是false的。按照DHTML编程宝典中的描述,对于Defer属性是这样写的:
Using the attribute at design time can improve the download performance of a page because the browser does not need to parse and execute the script and can continue downloading and parsing the page instead. 也就是说:如果是编写脚本的时候加入defer属性,那么浏览器在下载脚本的时候就不必立即对其进行处理,而是继续对页面进行下载和解析,这样会提高下载的性能。 这样的情况有很多种。比如你定义了很多javascript变量,或者在引用文件(.inc)中写了很多的脚本需要处理,那不妨在这些脚本中加入defer属性,对性能的提高肯定有所帮助。 举例如下:
<script language="javascript" defer>
var object = new Object();
....
</script>
因为defer属性默认是为false的,那么在这里
<script language="javascript" defer>
显式声明defer属性后等同于
<script language="javascript" defer=true>
声明了defer属性之后,需要判断是否有别的变量引用了defer脚本块中的变量,否则的话会导致脚本错误的产生。 引用
DEFER是脚本程序强大功能中的一个“无名英雄”。你可能从没有使用过它,但是看完这里的介绍后,相信你就离不开它。它告诉浏览器Script段包含了无需立即执行的代码,并且,与SRC属性联合使用,它还可以使这些脚本在后台被下载,前台的内容则正常显示给用户。
最后请注意两点:
1、不要在defer型的脚本程序段中调用document.write命令,因为document.write将产生直接输出效果。
2、而且,不要在defer型脚本程序段中包括任何立即执行脚本要使用的全局变量或者函数。
加上 defer 等于在页面完全在入后再执行,相当于 window.onload ,但应用上比 window.onload 更灵活!
比较下面的3个例子:
<button id="myButton" onclick="alert('ok')">test</button>
<script>
myButton.click();
</script>
<script defer>
myButton.click();
</script>
<button id="myButton" onclick="alert('ok')">test</button>
<script>
myButton.click();
</script>
<button id="myButton" onclick="alert('ok')">test</button>
---------------- 从w3school 上面看到-------------只有 Internet Explorer 支持 defer 属性。 ---------------- ------------------------------- ---------------- 从《high perfomance javascript》上面看到-------------只有 Internet Explorer4+和firefox 3.5+ 支持 defer 属性。 ---------------- -------------------------------
作者:kun10
发布时间:February 3, 2010
分类:JavaScript
很早就买了ppk的这本书,在还不知道taobaoUED 的时候就买了,当时也没有看得很明白,只是看到一些关于用户可用性的东西,觉得很不错,很快就用上了。下面就总结一下用户可访问性的规则:
1、条理分明的HTML 在脚本环境下面要保证可访问性的最显而易见的方法就是确保纯HTML页面包含了成功浏览所需要的“筋骨”,
内容、导航和重要的内容的表单都应该被硬编码(hard-code)到HTML中,这样,无论发生了什么,任何的用户都可以访问并使用它们。
这样,当浏览器不支持JavaScript的时候,会降低一些页面的易用性,但是其基本的功能不会被破坏。
1.1 、硬编码的连接必须有href你的html中任何的一个硬编码的连接都应该拥有一个href属性,他指向一个有用的页面或是其他的文件,因此这样是错误的
<a href="#" onclick="showPopUp('niceimage.png')">Nice Image</a>
当一个无脚本的用户点击了这个连接,什么也不会发生,坏例子 对此,我还要严重的批评一下山东毕业生就业信息网,它对于学生给出的就业意向,信息网给出了一些相关的推荐的岗位,但是居然利用了弹出窗口的方式,并且居然没有html硬编码,最可恨的就是他的javacript出错了,导致的问题就是这些学生非常需要的信息学生根本就不能访问,点击后只是提示javascript错误,也没有弹出窗口,也没有打开连接(所以说,真不知道是哪个没头没脑的家伙做的。如果是硬编码的话,至少有点技术的用户还可以分离出里面的html连接来访问这些信息)相反,一个清醒的javascript的开发者会这么做
<a href="niceimage.png" id="nice">Nice Image<a/>
document.getElementById('nice').onclick = function(){
showPopup(thie.href);
}
现在,无脚本用户沿用被硬编码的href属性,而脚步用户会打开一个弹出窗口(popup),网站保持了可访问性,而且把行为从结构里面分离了出来。
2、产生对脚本用户有意义的内容 在某些情况下,javaScript中产生的内容使网页更具有可访问性,
2.1、触发高级脚本的链接 假设你有一个链接,点击它将启动一段漂亮的Ajax脚本。这段脚本会获得内容并执行其他有用的任务。但是你没有一个合适的HTML页面可以让这个链接指向它。我们刚才已经看到了,
这样是不对的
<a href="#" onclick="startUpAjaxStuff()">我拿代码耍耍酷</a>
我们不能仅仅应用上一条规则(1.1) ,如果没有明显的于Ajax脚本等效的无脚本替代品,我们改把href指向哪里呢? 如果给链接加上一条href不可行,那么就用JavaScript来生成连接
var link = document.createElement('a');
link.href = "#";
link.onclick = startUpAjaxStuff();
var linkText = document.createTextNode('这回可以耍酷了');
link.appendChild(lintText);
document.body.appendChild(link);
如此一来,无脚本的用户就完全看不到这条连接了,这样很好,因为否则他点了这个连接却发现什么反应也没有,他会很困惑的。 注意,上面的例子把链接的href的属性设置成了'#',虽然我们知道使用#作为href通常不是一个好主意,但是我们需要它,因为绝大多数的浏览器定义一个超连接即使一个拥有href的a元素 幸运的是,在这种情况下,上一条规则并不适用,因为我们只是让支持JavaScript的用户看到了这个链接。能看到这条连接的用户,其浏览器一定支持onclick事件,所以href为#并无大碍。
2.2、用Javscript隐藏内容 隐藏内容是危险的。一般来说,隐藏的内容是你认为如果不立即显示可以提高页面可用性的内容。直到用户点击了页面里面的什么的时候,内容才显示出来。
如果没有JavaScript,内容就永远不会显示,而且这个页面就不具备可访问性,如果你创建了一个隐藏内容直到用户激活一段脚本的时候才显示,那么你应该始终用JavaScript来作这件事情,而不是在CSS中
有时候直接定义这样的css是不好的 :
.importantContent
{
display:none;
}
如果一个无脚本的用户访问你的页面,他将不会看到这些所谓的重要的信息,因此这个页面的可访问性就降低了,我想,一些页面所呈现的重要的信息,比如一些公告,应该设置css的时候就设定为display:block,然后用javascript去控制其隐藏和显示。,不过你在此处有什么反驳咱们可以探讨一下~~~值得一想的例子 ----例子---- Google的英文首页,在页面中定义了一个mousemove事件。这个事件只在用户的浏览器支持JavaScript的情况下面会工作(是上一个要点的范例),但我想说的不只是这个,这个鼠标移动事件会使得页面中除了主搜索框以外的一些部分显示出来,如果你不移动鼠标,而直接输入内容,使用的只是搜索框和搜索按钮,不过大多数的用户都是好动的,当他们移动了自己的鼠标的时候,页面上角如google邮箱,阅读器之类的服务连接都显示出来了,个人觉得这是很好的一种体验,不管怎么说至少晚一些运行了javascript。
----例子结束----
3、重定向用户 有时候,解决可访问性的问题就是为你的网站创建一个有脚本的版本和一个无脚本的版本。我不喜欢这个解决方案,显得很不帅。。。但是,就算不怎么帅,他还是很有实用价值的。
如果你使用的是这种方法,就应该遵循两点:
首先,你网站的入口应该是一个无脚本的页面(ppk这么说,其实我觉得到不一定就要做到纯无脚本,而是说做到无脚本可访问就可以,硬编码的什么的必须做到,这样其实第二条我们都可以不管了,很多时候我们应该在一个无脚本可访问的页面的基础上面去创建更好的可用性),这样,所有的浏览器都可以正常的访问;
第二,当浏览器到达这个无脚本页面,启动一段脚本来使得页面跳转到有脚本页面。我们在这里用replace来进行跳转。
<head>
<title>NoScript Page</title>
<script type="text/javascript">
var isSupport = [检测是否支持javascript的代码];
if(isSupport){//如果支持
location.replace("scriptpage.html");//最好是不要用location.href来做,原因见下面
}
</script>
</head>
如果在这里,你用了location.href,那么,它会在浏览器的历史记录里面建立一条浏览记录,如果用户到达无脚本页面,他就被跳转到了有脚本的页面,然而,一旦他点击了浏览器的后退按钮,就会被送回到无脚本的页面,而上述的跳转代码又被启动,他又被送回有脚本页面(这不是折腾用户是什么?) location.replace则会覆盖掉原来的页面,用户点击后退,会回到无脚本页面前面的那个页面,这也正好让后退按钮的功能给发挥出来了。所以,不管ppk说的这一部分靠不考谱(我是指把页面分为有脚本和无脚本两个版本的做法),我们都应该知道尽管有些功能效果很相近,但是,不乱用功能,是很重要的一件事,从选好replace和href方法做起。
4、键盘用户 我们已经看到了键盘用户不会发生鼠标事件(除非他们同时使用屏幕阅读器),因此,应该说你应该总是定义鼠标事件的替代方案。有时候这很简单,比如,把mouveover事件和focus事件配对。有时候则会困难些,比如一个托放事件,你就必须编写其他的函数来迎合键盘用户。
4.1、可点击元素 即使你创建了完美的键盘解决方案,如果用户不能把焦点移动到那些元素上面,那也是没有用的,(貌似有点废话阿,咱创建键盘事件不就是等待用户焦点移动来触发的么?但是我们要想的一个问题是用户是否能够将焦点移到元素上。)
我们设想一个下拉菜单,他使用mouseover事件来触发显示下一级的菜单,根据上面的原则,我们添加了focus事件,然后,为了触发focus事件,键盘用户必须把焦点落到下拉菜单上面,如果这一点做不到,那么脚本很明显是不可访问的。
在所有的浏览器中,可以可靠的获得焦点的元素只有链接(带有href的a)、表单域以及按钮(经过测试opera下面的表现很垃圾,都不想说啥,我的版本是9.64,居然不支持tab切换链接。。。)。因此,任何键盘友好事件都应该被设置在这几种元素上面。
5、<noscript>标签 浏览器厂商意识到开发者也许想提供特殊的内容给那些没有javascript解释程序的用户,他们发明了noscript标签。
noscript标签的工作如下所述
1、不支持任何javascript的浏览器不能识别这个标签,因为未知的标签会被HTML解释器忽略。这些浏览器会显示这个标签的内容。或许这里这和浏览器的渲染模式还有一定的关系。
2、支持javascript的浏览器会检查javascript是否背启用了。如果是,他们就从视图中隐藏这个标签的内容,否则就显示标签中的内容
我觉得这样的工作没有什么太大的用处,我基本不用这个标签~~~
作者:kun10
发布时间:December 20, 2009
分类:杂七杂八
今天看了一下chrome OS 发布会的视频,之前也看过,但是没有仔细看,今天好好的听了听google印度工程师的印度英语,呵呵,听得半懂不懂的,算是练练听力吧,看完之后有些想法,
首先:“chrome is chrome OS ”,这句话在演讲中被反复的提到了,google其实在做的一个事情就是计算机直接从浏览器启动,把所有的事情都放到互联网上干,记得视频里面说过一句话,大体的意思就是所有的东西都在互联网上,或许google已经做好了走云服务的准备了; 其次:觉得演示会挺简陋 的,演示的国际象棋游戏就是一个swf,音乐就是一个网页音乐播放器,最多就是点google自己的GTalk,wave一些产品,记得答记者问的时候,有些人提了如一些桌面应用能否在googleOS上面使用的问题,那个印度工程师看去对问题有些些的不爽,很潦草的带了一两句,然后说“下一问”,感觉有些回答的不礼貌,或许某些程度上还是看得出google有些心虚
总之,用谷歌的话来说,他们本身就没有打算做桌面级的应用,一个浏览器tab就是一个应用,其实就是说一个网页就是一个应用,或许这在某些程度上意味着我们应该在将来更加注重互联网应用的开发,不断的细化,做得像现在的桌面产品那样,很明显的,我们如果以目前使用互联网的方法去使用chromeOS会很不适应,因为网络的资源虽然多但是相对还是比较的分散的,如果资源得不到整合,google chrome不过是一个浏览器,如果说大家适应了打开电脑就是一个google首页(可以想一下hao123 的那种感觉,至少很明确能够找到你要的东西),然后开始一天的工作,谷歌的OS才会慢慢的获得成功。 另外从开发的角度说,JavaScript 应该会继续火下去(呵呵) 不管怎么样,不管chrome OS是否会成功,chrome is chrome OS ,chrome可进可退,成功了就是操作系统,失败了就是一个浏览器,就这么简单,可以说这次发布会谷歌也达到推广自己的浏览器的效果。。。
作者:kun10
发布时间:December 1, 2009
分类:JavaScript
最近在制作页面信息搜集时遇到了一个很棘手的问题,元素onmouseover事件的触发过于混乱,由于记录信息的页面结构比较复杂,一旦鼠标移出或是移入某个元素,都会触发一次onmouseover,如何准确的找到那些自己期望的srcElement和target成了一大问题,利用中饭后的时间看了一下组长的资料,发现很有用。讲的是页面元素的比较:
1、DOMElement.contains(DOMNode)---IE 这个方法起先用在 IE ,用来确定 DOM Node 是否包含在另一个 DOM Element 中。
当尝试优化 CSS 选择器遍历(像:“#id1 #id2”),这个方法很有用。你可以通过 getElementById 得到元素,然后使用 .contains() 确定 #id1 实际上是否包含 #id2。
注意点:如果 DOM Node 和 DOM Element 相一致,.contains() 将返回 true ,虽然,一个元素不能包含自己。
这里有一个简单的执行包装,可以运行在:Internet Explorer, Firefox, Opera, and Safari。
function contains(a, b) {
return a.contains ? a != b && a.contains(b) : !!(a.compareDocumentPosition(arg) & 16);
}
2、NodeA.compareDocumentPosition(NodeB)---W3C 这个方法是 DOM Level 3 specification 的一部分,允许你确定 2 个 DOM Node 之间的相互位置。这个方法比 .contains() 强大。这个方法的一个可能应用是排序 DOM Node 成一个详细精确的顺序。
使用这个方法你可以确定关于一个元素位置的一连串的信息。所有的这些信息将返回一个比特码(Bit,比特,亦称二进制位)。
对于那些,人们知之甚少。比特码是将多重数据存储为一个简单的数字(译者注:0 或 1)。你最终打开 / 关闭个别数目(译者注:打开/关闭对应 0 /1),将给你一个最终的结果。
这里是从 NodeA.compareDocumentPosition(NodeB) 返回的结果,包含你可以得到的信息。
个人认为的精华之处在此
Bits Number Meaning 000000 0 元素一致 000001 1 节点在不同的文档(或者一个在文档之外) 000010 2 节点 B 在节点 A 之前 000100 4 节点 A 在节点 B 之前 001000 8 节点 B 包含节点 A 010000 16 节点 A 包含节点 B 100000 32 浏览器的私有使用
现在,这意味着一个可能的结果类似于:
<div id="a">
<div id="b"></div>
</div>
<script>
alert( document.getElementById("a").compareDocumentPosition(document.getElementById("b")) == 20);
</script>
一旦一个节点 A 包含另一个节点 B,包含 B(+16) 且在 B 之前(+4),则最后的结果是数字 20 。如果你查看比特发生的变化,将增加你的理解。
000100 (4) + 010000 (16) = 010100 (20)
这个,毫无疑问,有助于理解单个最混乱的 DOM API 方法。当然,他的价值当之无愧的。
现在,DOMNode.compareDocumentPosition 在 Firefox 和 Opera 中是可用的。然而,有一些技巧,我们可以用来在 IE 中执行他。
// Compare Position - MIT Licensed, John Resig
function comparePosition(a, b){
return a.compareDocumentPosition ?
a.compareDocumentPosition(b) :
a.contains ?
( a != b && a.contains(b) && 16 ) +
( a != b && b.contains(a) && 8 ) +
( a.sourceIndex >= 0 && b.sourceIndex >= 0 ?
(a.sourceIndex < b.sourceIndex && 4 ) +
(a.sourceIndex > b.sourceIndex && 2 ) :
1 ) :
0;
}
返回4说明元素a在b的前面,2说明a在b的后面,20说明a被b包含,10说明a包含了b
写得很好的东西,当时直接拿来就能用~~~~
1