Archive for the ‘Web Developer’ Category

来自许诺的分享

在YUI2中,我们没有官方的时间模拟机制,但是YUI3带给了我们这个便利,使我们可以通过模拟来对代码进行最直接的重用:

我们可以很方便的模拟用户的以下行为:
  • click
  • dblclick
  • mousedown
  • mouseup
  • mouseover
  • mouseout
  • mousemove
  • keyup
  • keydown
  • keypress
  • blur
  • change
  • focus
  • resize
  • scroll
  • select

一个简单的例子:
YUI().use('node-event-simulate', function(Y) { 
     Y.one("body").simulate("click");}
);
这样就模拟点击了body的click事件。
当然你还可以附加其它按键,例如模拟shift键同时按下点击效果。
Y.one("body").simulate("click", { shiftKey: true });
同理alt键也是如此
node.simulate("click", { altKey: true} );
更方便的是你可以指定源
node.simulate("mouseout", { relatedTarget: document.body });
还有坐标
node.simulate("mousedown", { clientX: 100, clientY: 100 });
对于ui层面的改变它也同样敏感

YUI().use('node-event-simulate', function(Y) {
  var node = Y.one("#myInput");
  // 模拟节点选择改变
  node.simulate("change");
  // 模拟节点被选中
  node.simulate("select");
}
);

24th 十二月

来自陈漫凯的分享

更轻量

- 出色的颗粒化模块,子模块划分;

- 延迟加载;

- 强调代码重用(公共基类、插件、扩展);

更易用

- 统一的API;

- 便利(each, bind, queue, 支持链式);

更快捷

- 重构node, event, selector等,解决很多核心痛点;

- 依赖性处理;

Hello YUI

初识YUI3.x,最先发现的一个变化就是,原先YAHOO的命名空间被改为YUI,这样命名的修改,就不用在为了使用这个开源的框架而非要搞得代码里到处都是YAHOO公司的名字,但更好的一点是,可以借助不同的变量名让YUI3和旧版的YUI共存而不至于冲突。

沙箱(Sandboxing)

这是YUI3.x的一个显著特点,它可以在页面中创建一个多重开发环境。每一个YUI实例会自包含的,保护和限制。

YUI().use('node', 'event', function(Y) {
      // ready
});

在这里,YUI()执行并返回一个use方法执行后的库的实例。use方法至少有两个参数是必须,库组件名称和加载完毕后执行的回调函数。 上面的例子中,会加载名字为’node’和’event’的库组件,最后一个参数是回调函数,YUI()执行后的包含所加载的库组件的实例会以参数Y传给这个回调函数。

相对于YUI2.x的只有YAHOO一个全局变量,沙箱的这种多重开发环境的好处还是很明显的。

YAHOO.widget.HelloWorld = doSomething; // 开发者A的代码
...
YAHOO.widget.HelloWorld = doOtherthing; // 开发者B的代码

如上,在YUI2.x中,YAHOO作为全局变量,多人开发的时候,将会出现B的代码覆盖A代码的情况,而且这种情况不是使用闭包所能解决的。而YUI3.x的沙箱,提供每个实例可独立开发:

YUI().use('node', function(Y) {
      Y.HelloWorld = doSomething; // 开发者A的代码
});
YUI().use('anim', function(Y) {
      Y.HelloWorld = doOtherthing; // 开发者B的代码
});

沙箱使得代码更安全,而且,每一个实例的YUI3.x版本不同也没有关系。

YUI().use()的另外一个特点是每个实例的依赖,可以按需加载。这样不但可以节省带宽,而且也使得更加力度的颗粒化模块变得更有意义。而且,加载的所依赖的js文件是延迟加载的,加快了页面渲染的速度。

测试显示,延迟加载使得页面在更短的时间内渲染完成。

但同时,因为依赖进来的包,都是独立的文件,每引入一个文件,就创建一个<script>标签去请求js文件,这样大大的增加了HTTP请求的数量。

颗粒化模块

YUI2.x每个模块庞大的体积让不少人望而兴叹。比如有时候要使用日历里面的基础功能,你不得不把整个日历组件导人,整整75K的代码,你实际用到的仅仅是基础功能,这也是很多公司没有使用YUI的原因之一。YUI3.x做了大调整,对每个模块都进行了更细颗粒度的划分。比如,DOM模块,划分为了base, screen, style, selector-css2, selector-css3, selector-native等几个小模块,这样,我们可以根据需要选择所需的模块,对于我们控制页面的载入的数据量有很大帮助。

但更细的划分模块,带来的弊端就是,代码文件增多,维护成本变大。

选择器(Selector)

YUI2.9就已经引入了选择器引擎,但由于它在整个YUI2.x中出现得比较晚,所以,在很多基于旧版本的YUI项目中,还是用传统的元素匹配技术。

而在YUI3.x中,选择器已经成为了一种标准支持。

Y.one('#demo');
Y.all('.demo');
Y.all('.demo').filter([filter]);
Y.all('#demo li');

如果需要用到css选择器,需要把selector-css2.js和selector-css3.js引入。但从性能的角度上讲,我还是觉得没有必要去用那么复杂的选择器。

链式调用

跟jQuery一样,YUI3.x也开始支持链式调用,对于一些没有逻辑返回值的方法,YUI3.x都支持链式调用。

Y.one('#demo').set('innerHTML', 'Hello World!').addClass('hight-light');

链式调用并非YUI3.x的主打亮点,但的确带来了书写的便利性,但同时,对于向来严谨的YUI,链式一定程度上会带坏现有代码风格。使用YUI开发的人,至少要在链式调用使用程度上达到一定的共识。

Nodes

由于各个浏览器对DOM标准的支持不同,YUI Node对底层的DOM节点进行封装,并强化其功能,为创建、操作和获取DOM节点对象(集合)提供了丰富的方法。封装后的YUI Node和浏览器HTMLElement对象对比:

封装后,获取到的不在是一个浏览器DOM对象,所以对YUI Node对象的熟悉的访问和修改,必须通过getter和setter来操作。

var el = Y.one('#demo');
alert(el.id); // undefined
alert(el.get('id')); // 'demo'

小结

正如我们所看到的,YUI3.x相对于早前版本,完全是一个新的产物。通过新的语法,我们使用到的是,更轻量,更易用,更快捷的库。YUI2.x是一种工具集,而YUI3.x更像是一种架构,框架或者说解决方案。

本周的豆知识分享就来深入研究一下window.event对象。请先看看下边的代码片断。

<button id=”btn”>Click!</button>
<script type=”text/javascript”>
// <![CDATA[
    document.getElementById('btn').onclick = function (e) {
        if (typeof e == 'undefined') e = window.event;
        var target = (typeof e.target == 'undefined' ? e.srcElement : e.target);
        alert(target);
    }
// ]]>
</script>
上边是一种事件处理函数的常见写法,并且很好地处理了跨浏览器兼容问题。因此,变量target在不同浏览器下都正确地指向了被点击的button元素。但是我把代码稍微改成下边这种样子后..
<button id=”btn”>Click!</button>
<script type=”text/javascript”>
// <![CDATA[
document.getElementById('btn').onclick = function (e) {
if (typeof e == 'undefined') e = window.event;
setTimeout(function () {
var target = (typeof e.target == 'undefined' ? e.srcElement : e.target);
alert(target);
}, 1000);
}
// ]]>
</script>
我写了一个匿名函数,并让它在1秒后执行。由于我构造了一个闭包,1秒后执行的匿名函数也应该能正确地访问到变量e,而变量target也应该正确地指向button元素。然后我用比较流行的浏览器测试了一下上边的代码,最新版本的Firefox、Chrome和Opera下一切都如同预期,可是在IE系列(6、7、8)下,我只得到了一个JS错误——找不到成员。好吧,我需要分析一下原因了。因为这段代码只在IE下有问题,而且经过一些试验后我也排除了闭包引起问题的可能性,因此我把上边的代码简化成如下这种样子,以便突出问题的本质。
<button id=”btn”>Click!</button>
<script type=”text/javascript”>
// <![CDATA[
document.getElementById('btn').onclick = function () {
setTimeout(function () {
alert(window.event.srcElement);
}, 1000);
}
// ]]>
</script>

不辜负期望,以上代码同样产生了JS错误。然后我把alert(window.event.srcElement);改成了alert(window.event),这次终于没有错误了,而弹出的消息框里的内容是null。然后我又去翻了翻MSDN Library,看到了下边这段描述:

The event object is available only during an event—that is, you can use it in event handlers but not in other code.

也就是说,与其它浏览器在事件触发之后为每个事件创建一个单独的Event对象相对,IE的所有事件公用一个Event对象,也就是window.event。因此为了避免冲突,针对某个事件的window.event对象只在该事件的事件处理函数的执行过程中有效,一旦事件处理函数执行完了,window.event就被IE设置为null了。

到此为止,似乎问题的原因已经很明了了。但是细心的同学也许会反驳,IE其实也是为每个被触发的事件创建一个单独的Event对象,只不过每次都通过window.event来引用新生成的对象。因此我写了下边这段代码来验证这种观点。

<button id=”btn”>Click!</button>
<script type=”text/javascript”>
// <![CDATA[
var lastEventObj = null;
document.getElementById('btn').onclick = function () {
if (lastEventObj == null)
lastEventObj = window.event.srcElement;
else
alert(lastEventObj === window.event.srcElement);
}
// ]]>
</script>

按钮在第一次被点击时,该事件的Event对象被lastEventObj变量所引用。因此就算在按钮第二次被点击时window.event被设置为新的Event对象的引用,仍然可以使用lastEventObj变量来访问到第一次点击事件时的Event对象。如果IE在每次事件被触发时都创建一个新的Event对象,那么lastEventObj === window.event.srcElement应该返回false。如果IE为所有事件公用一个Event对象,只是在每次事件触发时重新设置该对象的属性的话,lastEventObj === window.event.srcElement应该返回true。经过测试,返回值是false。

问题开始变得有些诡异了。既然每次事件被触发时的Event对象都是不同的,在本文的第二段代码里边,的确是在变量e中保存了对Event对象的引用的。即使事件处理函数执行完毕后window.event被设置为null,在1秒后自动执行的匿名函数中仍然应该可以使用变量e来访问先前的Event对象,但是为什么会产生JS错误呢?问题的原因在这时再此变得扑朔迷离了。我做了一些试验后得到了一些惊人的结论,以下我直接列举出一些事实。

1、window.event对象不是真正的JavaScript 对象。

按照ECMAScript规范,JavaScript中只应该存在有两种数据类型——值类型和对象。而一切对象都应该衍生于Object对象,因此Object.prototype中定义的方法,例如toString,应该在一切对象上都可以调用。那么我们来看看下边的代码:

<button id=”btn”>Click!</button>
<script type=”text/javascript”>
// <![CDATA[
document.getElementById('btn').onclick = function () {
alert(typeof window.event.toString);
}
// ]]>
</script>

点击按钮后,弹出的消息框里竟然显示的是undefined。由此可见,任何标准的JavaScript对象应该包含的成员却在window.event对象上消失了。既然window.event已经不是一个标准的JavaScript对象了,所以如果有什么理所当然的事情在window.event上变得不对劲了也不要感到特别惊奇。

2、实际上,IE还是为所有事件公用一个Event对象。

我们直接使用下边的代码来说明问题。

<button id=”btn1″>Click1!</button>
<button id=”btn2″>Click2!</button>
<script type=”text/javascript”>
// <![CDATA[
var btn1EventObj = null;
document.getElementById('btn1').onclick = function () {
btn1EventObj = window.event;
alert(btn1EventObj.srcElement.id);
}
document.getElementById('btn2').onclick = function () {
alert(btn1EventObj === window.event);
alert(btn1EventObj.srcElement === window.event.srcElement);
alert(btn1EventObj.srcElement.id);
}
// ]]>
</script>

这次我们有了两个按钮,每个按钮都绑定了一个事件处理函数。我们先点击btn1按钮,该点击事件的Event对象的引用在btn1事件处理函数中被保存在了全局变量btn1EventObj中。然后我们试着用btn1EventObj变量来访问btn1的id属性,很好,弹出的消息框中的确显示的btn1。

接下来我们点击btn2按钮,在btn2的事件处理函数中我们做了一些测试。测试btn1EventObj === window.event时,不出所料,返回了false。看来window.event已经变成了另外一个Event对象的引用。然后我们再测试btn1EventObj.srcElement === window.event.srcElement,居然返回了true。等号左边的本来应该是按钮btn1,而右边应该是按钮btn2,现在它们居然相等了。然后我们再看看btn1EventObj.srcElement.id,结果它的值变成了btn2了。事情变得明了了,对象btn1EventObj与对象window.event之间的关系可以用下边的图来表示。

IE确实为每个事件创建了一个单独的Event对象,而window.event在事件处理函数的执行过程中也总是指向最新创建的Event对象。问题是每个Event对象的属性却共享的同一个属性值。在这个例子中,当按钮btn2被点击后,共享的属性值srcElement被更新为了按钮btn2。因此通过btn1EventObj.srcElement访问到的属性值也就被改变了。

3、被共享的Event对象属性值也只在事件处理函数的执行过程中才有效

在事件处理函数执行完毕后,并不仅仅是window.event被设置为null这么简单。可以看看如下代码:

<button id=”btn”>Click!</button>
<script type=”text/javascript”>
// <![CDATA[
var eventObj = null;
document.getElementById('btn').onclick = function () {
eventObj = window.event;
setTimeout(function () {
alert(typeof eventObj.srcElement);
}, 1000);
}
// ]]>
</script>
猜猜最后弹出的消息框里的是什么?居然是unknown。微软自家的JScript文档里的有如下定义:
There are six possible values that typeof returns: “number,” “string,” “boolean,” “object,” “function,” and “undefined.”
所以这个unknown代表着事件处理函数执行后,srcElement属性所指向的属性值已经变成了未知的什么什么了。不仅仅是srcElement属性,诸如clientX、altKey等Event对象的其它属性在事件处理函数执行完毕后也是如此。夜深人静一个人写周报的我面对如此灵异的事实也不仅感觉到背脊有丝丝凉意。但是如果我们如果像下边的代码这样,在事件处理函数的执行过程中把属性值保存在其它的变量中,则被保存的属性值在事件处理函数执行后依然可以访问到。
<button id=”btn”>Click!</button>
<script type=”text/javascript”>
// <![CDATA[
var target = null;
document.getElementById('btn').onclick = function () {
target = window.event.srcElement;
setTimeout(function () {
alert(target.id);
}, 1000);
}
// ]]>
</script>

由此可以,srcElement属性的属性值在这段代码中表现出了值类型的性质,因为事件处理函数执行完毕后,即使IE把公用的属性值设置为了未知的什么什么后,保存在变量target中的属性值并没有受到印象。至于为什么通过Event对象的属性来访问属性值时属性值表现出了对象类型的性质,这个就只有IE的开发人员知道了。

以上是本周分享的有关window.event的灵异事件录。虽然到最后还是以“只有IE开发人员才知道”这种半途而废的文字收尾了,但是至少我们可以得出一些有用的结论来帮助我们在今后写代码的过程中回避类似问题:

1、在IE中,不要在事件处理函数的执行过程以外的地方来访问Event对象及其属性。

2、如果非要访问,请在事件处理函数的执行过程中用闭包等方式把Event对象的属性的属性值保存在其它变量中。


注:此文版权属于 邓楠乔

按照W3C的定义,HTML中的注释以<!–开头,以–>结尾,但是真相并不仅仅如此。HTML是由SGML(标准通用标记语言)衍生而来,而在SGML中注释的定义如下:

一个注释以<!开头,以>结尾。注释中可以包含零个或多个注释块,每个注释块以–开头,并以–结尾。并且,注释块之间可以包含空格。

因此,以下注释都是正确的:

<!--Hello--> <!--Hello-- --World--> <!----> <!>

用正则表达式来表示的话,/<!(–[^-]*–)?(\s*–[^-]*–)*>/能够匹配的注释都是正确的。因此,下边这种诡异的注释虽然是一个正确的SGML注释,但是按照W3C的定义来说就不正确了。

<!--Hello---->World-->

反过来,下边两种注释是正确的W3C注释,但是却不是正确的SGML注释,因为包裹注释块的–没有配对。

<!---- >--> <!------>

然后问题就来了。Firefox按照SGML的标准来解释注释的。而IE、Chrome、Opera按照W3C的标准来解释注释。所以<!–Hello—->World–>这种注释在Firefox下正常,但是在其它浏览器下会把World–>显示在页面里。反过来,<!——>在其它浏览器里正常,但是Firefox下会把<!——>显示在页面里,因为Firefox认为这不是一个注释。另外,对于<!—- >–>来说,Firefox会认为<!—- >才是注释,把后边的–>显示出来,IE和Chrome下这个是一个正确的注释,而Opera会“智能”地认为<!—- >–>中间的空格是不小心多出来的,所以“好心”地把空格去掉后,把不适注释部分的–>给显示出来。

由于注释中间的中划线在各种浏览器中的表现如此不一致,因此为了避免种种意想不到的惊喜,最直接的做法就是避免在注释中包含中划线。

有的同学或许要问,这个或许是一个不错的知识,可是和我们的日常开发有什么关系呢?那么请看下边这段html:
<!--<a href="http://www.alibaba.com/products/used_cars/--1217----------------6-5574,.html">Sedan <span>(79727)</span></a>-->
在我们的网站里这种链接很常见,而且href的value是模板变量生成的。因此,如果我们在模板里这么写:

<!--<a href="${Url}">${Name}<span>(${num})</span></a>-->

可能觉得不会有问题,但是最后生成的页面在Firefox下就会把注释部分显示成下边这样:

Sedan (79727)-->

这个例子作为理由来讲的确很不充分,毕竟没有谁会用HTML注释来删掉页面里不需要的内容,但是说不定以后的什么时候这些关于注释的知识会在各位的工作中派上用场,这就是豆知识。

PS:如果有同学对HTML注释特别感兴趣,可以继续阅读本文的参考文献:

http://www.howtocreate.co.uk/SGMLComments.html

http://htmlhelp.com/reference/wilbur/misc/comment.html

Last Modified on Tuesday July 19th 2011 09:52:51 AM, Post Revisions:

This post has not been revised since publication.

注:此文版权属于 邓楠乔