Javascript Event 事件 特性 总结

Tagged Under : ,

简要提纲

  1. addEventListener,the 3rd parameter,true: parent to child,false: child to parent
  2. onMouseOver/Out,与 child 节点交互时,先 Out 再 Over
  3. style.display=none 的 Element,在某些情况下用 document.getElementById() 获取不到

Part 1

在支持 DOM2 事件模型的浏览器上,给元素绑定事件要用 addEventListener 这个方法,他有第 3 个参数:capturePhase。手册上的说明没太看懂,就自己做了个测试(在 http://leakon.googlecode.com/svn/trunk/leakon/javascript/event_phase 可以看到代码),明白了 true 与 false 的区别:当已注册的事件在某个节点上发生时,会按照某个顺序传递下去。true 指定由根节点向叶子节点传递,false 指定由叶子节点向根节点传递。

举个例子,有个 div,内部有个 img,2 个元素都注册了 onclick 事件,分别调用不同的处理函数。当你在图片上点击鼠标时,div 和 img 哪个先触发,就靠这个 capturePhase 来决定。div 是 img 的 parent,如果设置为 true,就是 div 先触发,然后才是 img。

不过一般来说都是设置为 false,即由叶子向根传递,因为 IE 就是这样做的,而且貌似不能改变顺序。

 

Part 2

还是父子节点事件处理的问题,这回讨论 onMouseOver 和 onMouseOut 事件。借用上面的例子,div 内部是 img。我们只给 div 注册一对 onMouseOver/Out ,当光标从 div 部分(这里假设 div 很大,而 img 只占 div 较少一部分面积)经过 img 的边框进入 img 面积内时,会先后触发 div 的 2 个事件:onMouseOut -> onMouseOver。也就是,div 认为鼠标是先离开 div 然后又立刻进入。

换到一个实际的应用场景,比如我们添加 Over 和 Out 事件就是为了给 div 添加一个高亮的样式表,div 背景默认是白色,鼠标划过变成红色,出去后恢复成白色。

要是你在 Over 和 Out 处理函数内部仅仅是 addClass 和 removeClass 这么一下,那你会看到跳动的感觉,也就是鼠标按如下路线行进:inDiv -> inImg -> OutOfImg -> inDiv ,别看鼠标一直在 div 内部,你以为不会有 onMouseOut 事件,事实不是这样,光标进入 img 的那一瞬间,就先后执行了  Out 和 Over 函数。而且,有些情况下,后续的 Over 函数没能得到成功执行,现实是 div 的样式表移除后就没能再添加上。

 

Part 3

这个问题大家应该早就知道,只是我今天才发现。我以为 document.getElementById() 跟 DOM 节点当前的样式无关。今天给一个 Element 注册事件的时候发现找不到该节点,原因是那个节点的 display 样式设置为 none。

按说不应该是这样,可能跟当时运行的环境有关,也可能是我绑定事件的函数有问题,总之看到的现象是跟样式定义有关。总结在这里,以备日后回忆,参考。

 

我现在觉得 Javascript 最难的就是事件处理这部分了。这里的事件不是简单地给某个 div 绑定一个函数来响应鼠标点击,而是一整套的处理流程和逻辑。

事件发生源、事件类型、传递方向、再哪个节点上应该停止传递,等等。

当页面中元素很多,关系错综复杂时,就需要一个事件处理框架来帮忙。我们需要的是结构清晰逻辑合理的代码,而不是满篇重复地 if else 判断。

举个简单的例子,如果你做一个邮件系统,类似 Gmail 那样的,你每点一封未读邮件,或者给某些邮件加上 tag,都会触发一系列的关联事件,例如要把未读邮件总数减少,给 tag 列表里添加新项目,同时用突出的颜色高亮选中的邮件标题,还要在页面顶部给出处理成功的提示,甚至还要问用户是否要撤销刚才的一系列动作。

天啊,看这 2 行文字描述我就觉得很晕了,把他用代码实现出来,并处理各种异常,还要保证代码的可读性,我只能说我自己能力还差得太远太远。

前两天听说 MooTools 这个 Javascript 框架在面向对象方面做的不错,当项目越来越复杂时,按照 MooTools 方式来组织代码可以有很好的可维护性。

找到了电子书,已经放到 blog 中可以下载了。

带链接的 img 标签不宜应用 margin 或 padding 样式

Tagged Under : ,

上述5个配图,分别是IE6、Firefox3、Opera9、Safari3和Chrome浏览器下的不同效果。

应用场景是,图片有链接,HTML片段是:

<a href=”…”><img src=”…” alt=”…” /></a>

img 标签有样式表定义,为了使图片与周围的内容有一些间距,我加入了 margin 定义。

加入之后,在现代浏览器中,会出现上图中显示的蓝色 hover 块与图片不匹配的情况,在这点上,IE6、IE7都能正常显示。

定位问题用了很久,最后发现是 margin 或 padding 的问题。

现在不知道是为什么,只知道解决的办法是去掉这2个样式定义。

如果需要图片有边距,可以在外层套上 p 标签,给 p 标签应用 margin 或 padding 可以达到同样的效果。

MySQLi vs MySQL

Tagged Under :

Background
Between participating in a MySQLi group and telling others about PHP5’s new MySQLi (MySQL improved) extension, there always seems to be a redundant (though valid) question, “Why?” Simple enough right? Maybe not, so I attempted to come up with some good benchmarks. Now note, just because of the pure nature of PHP extensions, this is easier and harder in some ways than doing let’s say, “ATI X800 vs Nvidia 6800″ type benchmarks. 

With that said, I tried my best to keep the test scripts used identical down to the variables. There will obviously be some differences as conceptually some things are different between the two extensions.

The Environment 
The benchmark hardware/software configuration is as follows:
     Processor - Intel Pentium 4 (HT) 2.6 gHz/800mHz FSB 
     Memory - 1236836K (~1.2GB) PC3200 DDR 
     Hard Drive - Seagate 250GB (7200 RPM) SATA150
     Operating System - FreeBSD
     PHP - mod_php5 with Apache2 and Zend Optimizer 
     MySQL - mysql-server4.1.11

Hardware wise, it’s not the fastest server on the face of the planet, nor is it the slowest. In fact, I think it’s a mid-level system which makes it perfect for benchmarking. I used Apache2, as I believe more people are starting to use it. I may do a future benchmark with Apache1.3.x as I do have the binaries in place. Apache will not be accepting external connections. The MySQL server has networking off. FreeBSD is running with soft updates on.

The tests 
I spent some time trying figuring out what would be a good suite of tests to run. Without saying, they’d all obviously have to be PHP scripts of some sort interacting with MySQL through the traditional MySQL extension and the new MySQLi extension. The real question was, how these scripts would be setup. I decided to take the procedural approach (though I recommend using MySQLi in the OO way), since the traditional MySQL has no OO support. Below is my attempt at it.

The MySQL table
Query caching is off.
The table structure is as follows:

DESCRIBE benchmark;
+------------+--------------+-----+----------------+
| Field      | Type         | Key | Extra          |
+------------+--------------+-----+----------------+
| PRIMARY_ID | int(11)      | PRI | auto_increment |
| FOO        | varchar(255) |     |                |
| DUMMY1     | text         |     |                |
| DUMMY2     | text         |     |                |
| DUMMY3     | char(1)      |     |                |
+------------+--------------+-----+----------------+

 

Note, the MySQL table structure should not matter for our benchmark, as we are simply executing SQL queries through different mediums.

The PHP scripts
The focus of the scripts will be around the INSERT clause. Why not SELECT you might say. Well it doesn’t matter quite frankly. We are here to benchmark the functions with identical SQL, the key phrase is “identical SQL”.

Insert SQL test - Executes an INSERT query 100,000 times on the `benchmark` table with a for loop. mysql_query and mysqli_query will be used.
Insert SQL test 2 - Builds 100,000 INSERT SQL queries into one string. mysqli_multi_query will be used.

`benchmark` is truncated after each test script runs.

How performance is guaged
Deciding how to guage performance was undoubtely the hardest part of this benchmark. I had really only 2 options. The first, to setup a simple timer in between the functions where the queries took place and calculate the difference. The other option, was to use a full out profiler. I decided to go with a profiler, as it would give me execution times for surrounding PHP code (for example, the for loop).

My two (considered) choices in the profiler category are xdebug and Zend Studio Debugger. Let me say right now, I use Zend Studio for all my PHP work and so I had a certain bias to use it’s debugger, but alas…this benchmark must do without bias! This is about performance! So I hit Google to try to make an unbias decision about this. I found that Zend Debugger had won the best debugger award from PHP Magazin and so I decided to use it for this benchmark. Now note, all profilers are pretty much the same, in that conceptually they do the same thing. So choosing one over the other for this benchmark would not have a made a difference.

Running of the tests 
So with all the formalities out of the way, it’s finally time run the test scripts. Getting anxious are you? Don’t worry, so am I :-)

  • Insert SQL test (insertsql_mysqli.php)
    Here is the run of the first profile

    A list of 9 subsequent runs and the DoInsert() function’s “own time” results (in microseconds) 

     
    DoInsert() own time
    Total execution time
      24,670.14 26,075.74
      25,132.45 26,569.09
      25,232.18 26,547.64
      25,006.92 26,289.18
      25,401.54 26,710.59
      27,792.51 29,136.76
      25,386.73 26,814.82
      25,279.91 26,575.78
    Average 25,504.32 26,859.87
    Standard Deviation 849.09 846.19


    The standard deviation is an acceptable figure, on average give or take 1 second between those runs. Then on average, DoInsert() takes about 25 seconds to run with mysqli_query. Now let’s try mysql_query.

     

  • Insert SQL test (insertsql_mysql.php)
    Here is the run of the first profile

    A list of 9 subsequent runs and the DoInsert() function’s “own time” results (in microseconds) 

     
    DoInsert() own time
    Total execution time
      24,799.11 26,134.51
      25,075.20 26,426.23
      24,685.38 26,048.15
      24,891.70 26,245.90
      25,091.64 26,299.66
      24,713.04 26,138.62
      24,741.54 26,125.89
      24,596.36 25,970.21
    Average 24,885.75 26,007.04
    Standard Deviation 229.16 489.50

    The standard deviation with mysql_query is much less. In fact, mysql_query is able to do our task about 1 second faster than with mysqli_query. Let’s try to do the same task withmysqli_multi_query.

     

  • Insert SQL test (insertsql_mysqli_multi_query.php)
    Here is the run of the profile

    53 seconds, mysqli_multi_query definately is not the best choice here. 

     

  • Insert SQL test with MySQLi statements (insertsql_mysqli_stmt.php)
    In this test, there is a source code change conceptually, so make sure you take a look.
    Here is run of the first profile

    A list of 9 subsequent runs and the DoInsert() function’s “own time” results (in microseconds)
     

     
    DoInsert() own time
    Total execution time
      24,077.77 25,179.06
      24,535.49 25,687.64
      25,232.18 26,547.64
      24,476.72 25,552.14
      24,445.86 25,551.81
      24,586.98 25,703.18
      24,582.58 25,675.57
      24,783.44 25,931.73
    Average 24,589.72 25,760.13
    Standard Deviation 318.49 376.39


    The standard deviation is an acceptable figure, on average give or take half a second between those runs. Then on average, DoInsert() takes about 24 seconds to run withmysqli_stmt. I think we have enough for now that we can come to a conclusion.

 

Conclusions
The data showed that mysql_query outperforms mysqli_query head to head. For the specific task of inserting 100,000 rows into a table, using mysqli_multi_query more than doubled our script execution time. At this point, you may start wondering what the point of using MySQLi is if the traditional MySQL extension is getting the upper hand on MySQLi.

However, using MySQLi statements we got better results, in fact it outperformed mysql_query by as much as mysql_query outperformed mysqli_query. Though this might not be enough reason for you to change existing code, remember that MySQLi statements are “pre-escaped”, meaning that there is no need for you to do mysql_real_escape_string on any of the variables. It’s apparent that given this fact about MySQLi statements, if we had made the extra function call of mysql_real_escape_string to the four inserted values, we would have greatly increased our execution time (good for character escaping user input and stopping SQL injection).

While the above may not be enough reason to change existing code to use MySQLi statements, they might be good reasons to use them in the future, for both security and performance. 

[11/16/2005] Also just to mention, mysqli also implements ssh like security measures to make logins between the client and server much more secure.

In summary,

mysqli_stmt > mysql_query > mysqli_query > mysqli_multi_query

Javascript 字符串 数字 浏览器 性能差别

Tagged Under : ,

我在做一个 Javascript 版的公历农历转换程序,功能完成后,需要对性能做优化。计算算法是在网上找的,原作者对历法的转换很精通,但对程序性能没有仔细考虑,当计算量大的时候,会给用户的浏览器造成很大压力,给用户的感觉就好像浏览器假死一样。

我花了2天时间在性能优化上,并在不同的浏览器下做对比测试,发现一个比较重要的差别,IE、Firefox 和 Chrome 之间的区别很大。

通常,生成一个月的日历,最多需要计算42天(可参考Google Calendar,每行代表一周7天,最多可能有6行)。

性能测试的时候,我让程序连续计算3年,共36个月,1000多天。

总的运行时间是:

IE7: 2.828 s
Safari3: 1.016 s
Firefox3: 0.885 s 
Opera9: 0.765 s
Chrome: 0.281 s

这下终于见识了 Chrome 采用的 Javascript V8 引擎的强大实力! 

以上统计结果都是测试3次取结果平均值。

程序运行到这个时间量级,已经是我优化过了的,最开始的程序,完成2年的计算,也就是上述运算量的三分之二,Firefox3用了8秒左右,Chrome很棒,用了2秒,而IE,30秒过后,浏览器直接问我是否要强制停止脚本运行,实际上我还从来没有在IE上成功执行过!

关于浏览器性能的差别,你可以用IE和Chrome打开淘宝的同一个比较复杂的页面,感受一下拖拉机与飞机的区别。

上面是浏览器的差别,下面我再给一些 Javascript 程序上的差别。

我的农历转换算法,有一个关键函数,需要多次调用,这个正是性能的瓶颈所在。

观察了调用方式和次数,这个函数运行了33000多次,而总共参数的变化只有1000次左右,如果加入缓存,可以减少97%的运算量!!!

我采用hash表的方式对每个输入的key做缓存,由于函数的参数是年月日,共3个,所以需要把这3个参数拼接成一个key。

最开始,我用字符串拼接,y m d 之间用 “_” 下划线连接起来,但仅仅是一个这样的字符串操作,程序运行时间就从0.8秒增加到1.1秒,增加了将近40%的时间!!

这是不能接受的,这将使我的缓存优化方式变得毫无意义。

农历算法的大部分关键代码,都是数字上的计算,代码很多,但好像没有产生性能瓶颈,这给了我启发。

我改用数字索引,把 y m d 用加法和乘法算出类似于 20081026 这样的整数,这样运行时间从0.789 s增加到0.853 s,只有不到10%的增加。

我的缓存,只能把运行时间降低20%到30%,所以用整数索引,可以达到优化的目的。

http://leakon.googlecode.com/svn/trunk/leakon/javascript/wannianli/nice/

很愿意与大家一起分享,一起分析,一起应用~~

原创文章,请尊重我对互联网的贡献,谢谢!

position static 屏幕滚动

这个黄色背景的提示框,样式定义中 position 设置为 static,因此可以精确地定位到输入框的下方,并且覆盖住原有的内容。达到这样的效果,static 值是必须的,他使该元素脱离于原有的元素布局,可以浮动到父层容器内的任何位置。

请注意这个父层容器,通常 static 元素只能在父层元素的容器内显示,具体位置用 left 和 top 属性决定。

这本来没什么问题,不过,当浏览器的窗口不能完整容纳整个页面,而出现滚动条时,就会出现问题。

如果提示框定义在 body 元素内,那么当滚动页面时,提示框会保持与浏览器窗口的位置不变。但此时提示框与页面元素的相对位置会发生变化,也就是相对位置不同步了。

就拿图示的例子来说,向下拖拽滚动条,文字输入框会向屏幕上方移动,但黄色提示框则保持不动,因为他的 position 相对位置的参照物是浏览器,而不是页面顶部。

如果想让输入框和提示框保持同步,则需要把提示框定义在与输入框相同的父层容器内。

这时 position 的 left 和 top 的参照物就是父层容器了,拖拽滚动条,输入框和提示框的相对位置会保持不变。

从合理的角度来说,如果整个页面有多处需要提示框,则应该在 body 下面定义一个全局的 div,在需要显示的位置,通过绝对定位调整提示框的 left 和 top。

但这样没法保证页面滚动时提示框与预期位置同步。

在每个需要提示框的地方,都定义一个独立的输入框,则可以避免同步的问题,但页面会存在过多的 dom 节点,容易造成浏览器内存泄露,或者导致浏览器打开页面速度缓慢。

十年

昨天参加初中同学聚会,看到了十年没有见到的老同学。大家还都是老样子,变化不大。有些已经结婚了,有的马上要结婚。算上这个,变化就太大了。

迎风中学,初四5班。

我们的学校已经没有了,我们的教室也变样了,不过我们43个同学团结在一起度过的四年初中生活,是我们此生永远不会忘记永远不会改变的回忆。

这四年中,我当了三年班长,可能这是我给大家留下的印象比较深刻的回忆了,虽然我这个班长没有以身作则,没有给大家往好路上带。

记得那会儿,罢课、逃课、打架……,干坏事儿的时候总有我,而且其中大部分都是我带头……

昨天的聚会,我迟到了。当我同时看见12位久未谋面的同学的时候,一种特别亲切的感觉一下子把我带回了十年前。聚会前因害怕好久不见会有些生疏而产生的担心,在那一刻瞬间消失。

是的,我们5班的同学,就算再久没有见面,聚在一起,也不会有生疏的感觉!

初中同学之间的关系,应该是最单纯最真切的,没有勾心斗角,只有嬉笑怒骂。

就像张晶说的那样,我们走进社会后,会接触不同的人,结交不同类型的朋友,但那份感觉,跟初中同学的感觉是完全不同的。初中关系最好的朋友们,在十年后的今天,仍然是关系最好的朋友。我跟几个发小之间的友谊就是在初中时代建立起来的。虽然我们也不是经常见面,但每隔几个月,大家都会找机会在一起聚聚,聊聊,不会让距离淡化感情!

聚会比较有意思的一个环节,是在KTV唱歌,大家玩了一会儿后,都停了下来,每人说说聚会的感言。说着说着,大家就开始复原在回忆中保存了十年的故事,你说一句,我补充一句,让我们有些模糊的记忆又回到了最新鲜的状态~~

想说的话太多太多,积累了十年的感情,无法在几个小时内全部释放。

我离开那间教室,已经十年。

但我每次在梦中延续校园生活时,地点总是定格在那件虽然破旧但干净整齐的教室。

本来这次聚会通知了20多个人(在此表扬一下亚旎和张晶为组织大家聚会付出的努力),但由于各种原因,有些同学没能参加,我们都很遗憾。不过我知道大家都很期盼着下次聚会能见到更多曾经最为熟悉的面孔。今天是征宇举办婚礼的日子,昨天我们聚会的时候,大家都为你举杯庆贺!我也再次祝你新婚快乐!

希望5班的同学们都能身体健康,工作顺利,家庭幸福,班长祝福你们!

Google IP

Tagged Under : ,

Google 的 IP 再次被封!

216.239.32.21 
216.239.34.21 
216.239.36.21 
216.239.38.21

这些是原有Google的IP列表,现在32和36已经被无耻的GFW封了。

我whois查了一下这些IP:

OrgName:    Google Inc. 

OrgID:      GOGL

Address:    1600 Amphitheatre Parkway

City:       Mountain View

StateProv:  CA

PostalCode: 94043

Country:    US

 

NetRange:   216.239.32.0 - 216.239.63.255 

CIDR:       216.239.32.0/19 

NetName:    GOOGLE

NetHandle:  NET-216-239-32-0-1

Parent:     NET-216-0-0-0-0

NetType:    Direct Allocation

NameServer: NS1.GOOGLE.COM

NameServer: NS2.GOOGLE.COM

NameServer: NS3.GOOGLE.COM

NameServer: NS4.GOOGLE.COM

 

比较可喜的是,Google的IP范围还算不少,从32.0到63.255,应该够GFW折腾的了。

如果你想使用Google的绑定域名服务,那你必须把你的域名CNAME到ghs.google.com。

但这个域名的IP早就被封了,你只能自己添加一个A记录,把IP改为上述范围中的一个能用的值。

然后再把你的二级域名CNAME到你刚添加的A记录上。

如果你嫌麻烦,我可以给你提供一个免费的Google域名主机:

google-host-00.leakon.com

我会写程序定期检查这个IP是否能ping通,如果失败,我会立刻更换这个域名的IP。

你需要把域名指向Google的话,可以CNAME到我这个域名,以后就可以不用再担心被封的问题。

如果IP被封,我这边可以在24小时内保证新IP生效,对你的应用来说,这一切都是透明的。

万恶的GFW,不干缺德事,你就会死吗?

Zend Optimizer not installed 问题解决

Tagged Under : ,

今天土了一把,遇到很简单的问题,却用了很久才搞定。

yoyoxuebaobao 装 SupeSite,提示 “Attention:zend optimizer not installed”,可是明明安装了,用 phpinf() 检查,也提示:

This program makes use of the Zend Scripting Language Engine:
Zend Engine v2.2.0, Copyright (c) 1998-2007 Zend Technologies
with Zend Extension Manager v1.2.0, Copyright (c) 2003-2007, by Zend Technologies
with Zend Optimizer v3.3.0, Copyright (c) 1998-2007, by Zend Technologies

这让我很恼火。

到网上查了半天,原来是 php.ini 中 zend_optimizer.enable_loader 的值设置为 0,也就是默认没有开启!

设置为 1 后立刻搞定~~

问题很弱,希望能给遇到同样问题的朋友们提供帮助~~

PHP 安全设置 %5c magic_quotes_gpc GBK

Tagged Under : , ,

安全问题。

今天看到一个朋友的blog上,写到PHP的安全设置,应该开启magic_quotes_gpc,避免SQL注入等。

可是,我在用Symfony开发时,文档上说应该关闭php.ini的magic_quotes_gpc,以前我不知道为什么,今天突然有些明白了。

这个设置,只能起到非常有限的安全作用,却有可能带来更严重的问题。

PHP的放置SQL注入,很多网站或教程上,都写着用addslashes或mysql_escape_string来过滤,或者用自动的magic_quote。这些函数的作用,就是查询字符串中的单引号、双引号和斜线”\”,在这些字符前面插入一个斜线”\”。

如果遇到了”頫運鳿黒靄錦鳿黒靄錦”这样的字符,而你的数据库、网页又是GBK编码的话,上面的3种过滤函数,都会给你带来麻烦,因为这些字符的第二个字节的编码,是%5c,恰好是”\”对应的编码。

原本是2个字节的汉字,被这些函数变成了3个字节,最后的2个字节是连续的”\”,而这第3个”\”会把后续的引号转义,因此SQL语句就会出错,导致更新无法执行。

安全建议:

  1. 设置php.ini,默认关闭magic_quotes_gpc
  2. 使用mysql_real_escape_string对字符串变量进行转义,注意,转义前判断magic_quotes_gpc是否已开启
  3. 使用utf-8编码,可以有效避免此类问题

执着

Tagged Under :

为了Yoyo开心、幸福,我愿意断绝与Z的所有联系。

我愿意!

Google

Google
LAMP-Linux-redhat LAMP-Apache LAMP-MySQL LAMP-Php Leakon-Wiki Leakon-BBS XueBaoBao Xyoyou