请使用非IE浏览器访问!
优米网内部JS教程。
上期讲了JS里面变量、函数、变量作用域等。 这次讲一下浏览器环境下的一些实用的知识
Event 事件
如何绑定事件
可以在HTML源代码中用DOM元素的属性直接声明。例如:
<input type="text" size="30" id="email" onchange="checkEmail()" >
也可以在JS里绑定,例如:
document.getElementById('email').onchange=function()
{
checkEmail();
}
有什么区别呢?
先说一下JS里可以用new Function("inner code")来创建一个函数。即可以把字符串形式的源代码转换成可执行的code。 写到HTML源代码上的事件代码可以理解成事件属性的值被new Function()了。 所以onchange="return true;" 就相当于 dom.onchange=function(){ return true; }。
值得注意的是dom.onxxxx=function 的绑定方式是粗暴式的,后绑定的函数会覆盖掉前面绑定的函数。如果需要同一个事件绑定多个函数, 可以使用dom.addEventListener(w3c)和dom.attachEvent(IE)。(又是讨厌的IE与众不同!)
要实现搜索doc文档,第一步就要先从二进制doc文件中提取出文本内容。现在一般的做法是用微软的com组件,或者用第三方的软件来提取,都不太方便。 所以我想用php直接提取其中的文本。
而微软早年搞的doc二进制标准实在是太复杂了,即使微软在2008年公布了doc文件的二进制编码格式,但是要想从中按照正规的方法提取出文本内容仍然是一件非常困难的事情。我的感觉就像是小时候玩RPG游戏寻宝一样,必须一步步按照微软的寻宝图路线走,任何一步错误都拿不到宝藏。。。
研究正规途径提取文本并取得一定成功的是Ethan同学,他通过相关文献资料,找到一条可以用PHP分析doc二进制文件并从中提取出文本内容的方法。但是只能提取出一少部分doc文档的内容(可能是实验做的太少)。我后来在他的基础上又研究了几天,发现要想走正规路线拿到文本非常困难,因为doc文档编码中文和英文不一样,还分为1Table和0Table等。
昨天突发奇想,能不能用暴力破解的方式拿到文本呢? 经过一天艰苦奋战,终于取得了可喜的成绩。 这里是DEMO。
暴力法的大概思路是: 每512个字节分为一个sector,每个sector为一个基本单位。然后发现,如果某个sector是用来存储文本的,那么此sector里面就只有文本内容,没有其他数据。 那怎样判断是否为文本sector呢? 我引入了一个常用汉字和英文字母以及标点符号组成的数组。判断此sector里面常用文本占sector总长度(512字节)的比例(根据大量测试,比例在0.3以上,基本上可以确定是文本sector),就可以确定整个文件的文本内容。 另外,doc文件里,汉字是用2个字节来存储的,而且用的little endian方式(就是字节顺序是反的)存储的,但是英文是用1个字节。 后来又想了一些办法来判断是中文还是英文。 最终经过大量的测试,绝大部分文件可以正常提取绝大部分文本(偶尔可能最有一个sector文字太少无法识别)。 对于索引doc文件这个简单的需求来说,已经足够了。
完。
今天遇到一个cookie的问题: 名叫username的,域为www.umiwi.com的cookie无法删除,导致用户登录以后无法退出。搞了半天终于发现问题所在:原来是我关于cookie的基础知识没搞清楚
。。。。
Cookie不仅仅有名字和值两个属性,还有域(domain),过期时间(expires),路径(path)等属性。 其中,不同的域、不同的路径下可以存在同样名字的cookie。 比如这个页面: 用鼠标戳我
一般我们删除cookie的方法是用一个同样名字、过期时间为过去某个时候的Cookie覆盖之。 这时就一定要搞清楚你要删除的cookie的域和路径,Cookie域和路径要一样才能被覆盖。 否则产生的效果就是那个想要被删除的Cookie具有神奇的生命力,无法被清除~~~
另外,我发现在设置Cookie的时候,如果没有指定域名,那么设置的这个cookie的域默认为当前域,比如www.umiwi.com。 如果设置的时候指定了某个域,那么浏览器存的时候会自动在前面加.。比如PHP代码: setcookie('test','a',0,'www.umiwi.com'); 那么 浏览器里面存的Cookie的域为.www.umiwi.com。 所以,最好的方式是设置Cookie的时候怎么写的,删除的时候就怎么写。 要不就会出现我遇到的情况: 明明浏览器里看有 username=longbill,域为www.umiwi.com的Cookie,但是在php里写 setcookie('username','',time()-1000,'www.umiwi.com','/'); 无法删除那个Cookie。 原因就是我删Cookie的操作其实上是发了一个新的名叫username、值为空、过期时间为过去1000秒、域为.www.umiwi.com、路径为/的Cookie。这个Cookie发到浏览器马上就过期了,什么也看不到。而我要删除的在www.umiwi.com上的cookie还活的好好的。。。
补充一下HTTP的知识: php里面的header函数发送的header信息默认是不重复的,后面发的会自动覆盖前面的内容。一般说来HTTP请求的header部分也是没有重复信息的,除了Set-Cookie头! 所以,如果我们使用header函数来发送cookie,请注意加第二个参数true。 详见header函数。
完。
昨天不小心看到了Google Javascript Style Guide,Google在里面约定了一些名字的书写规则,也给出一些更好的建议。 下面我翻译一段个人认为很有价值的东西:
布尔表达式
以下代码在布尔表达式里都等于false:
-
null -
undefined -
''空字符串 -
0数字
但是请注意,以下这些等于true:
-
'0'字符串 -
[]空的数组 -
{}空的对象
这就意味着本来这样的代码:
while (x != null) {你可以这样来写(当你不希望x为0、空字符串、false时):
while (x) {当你希望检查一个字符串是否为空的时候你可以这样:
if (y != null && y != '') {但是这样会更简洁更高效:
if (y) {注意: 有很多容易搞错的布尔表达式(以下表达式的值都为true):
-
Boolean('0') == true/* Longbill注释:
'0' != true
* if ('0') alert('yes');
* if ('0' != true) alert('yes');
* if ('0' == false) alert('yes');
* 以上三个都会弹出yes!!
*/ -
0 != null
0 == []
0 == false -
Boolean(null) == false
null != true
null != false -
Boolean(undefined) == false
undefined != true
undefined != false -
Boolean([]) == true
[] != true
[] == false -
Boolean({}) == true
{} != true
{} != false -
Longbill 补充下:
NaN != true
NaN != false
Boolean(NaN) == false
三目运算符
这样的代码:
if (val != 0) {
return foo();
} else {
return bar();
}可以被简化成这样:
return val ? foo() : bar();三目运算符在生成html字符串的时候也很有用:
var html = '<input type="checkbox"' +
(isChecked ? ' checked' : '') +
(isEnabled ? '' : ' disabled') +
' name="foo">';&& 和 ||
由这两个逻辑运算符连接的表达式会从左到右依次执行,直到遇到满足条件为止。
所以"||" 又被称作 '缺省值' 操作符,因为如下代码:
/** @param {*=} opt_win */
function foo(opt_win) {
var win;
if (opt_win) {
win = opt_win;
} else {
win = window;
}
// ...
}可以被简写成:
/** @param {*=} opt_win */
function foo(opt_win) {
var win = opt_win || window;
// ...
}"&&" 同样很有用。比如,如下代码:
if (node) {
if (node.kids) {
if (node.kids[index]) {
foo(node.kids[index]);
}
}
}可以被简写成:
if (node && node.kids && node.kids[index]) {
foo(node.kids[index]);
}或者这样:
var kid = node && node.kids && node.kids[index];
if (kid) {
foo(kid);
}甚至可以这样:
node && node.kids && node.kids[index] && foo(node.kids[index]);
使用 join() 来创建字符串
一般来说,累加字符串是很常见的:
function listHtml(items) {
var html = '<div class="foo">';
for (var i = 0; i < items.length; ++i) {
if (i > 0) {
html += ', ';
}
html += itemHtml(items[i]);
}
html += '</div>';
return html;
}但是这种方式在万恶的IE浏览器下会很慢,通常以下代码会更快:
function listHtml(items) {
var html = [];
for (var i = 0; i < items.length; ++i) {
html[i] = itemHtml(items[i]);
}
return '<div class="foo">' + html.join(', ') + '</div>';
}注意:指定数组的下标来赋值会比直接用数组的push()方法更高效。
遍历Node List
在遍历Node list 的时候,常常会用到length属性来当循环的边界条件。因为循环的时间复杂度是O(n),而每循环一次,都要检查一下边界条件(length属性),这样时间复杂度变为O(n^2):
var paragraphs = document.getElementsByTagName('p');
for (var i = 0; i < paragraphs.length; i++) {
doSomething(paragraphs[i]);
}这样做会更高效(时间复杂度应该是O(n) ):
var paragraphs = document.getElementsByTagName('p');
for (var i = 0, paragraph; paragraph = paragraphs[i]; i++) {
doSomething(paragraph);
}以上这个方法同样适用于对象和数组(当数组不含有会被当成是false的值的时候)。
/* 原文出自:Google,翻译: Longbill (http://php.js.cn) 2010-10-19 */
另外,当你遍历一个Node的所有子Node的时候,可以用firstNode和nextSibling属性,就像这样:
var parentNode = document.getElementById('foo');
for (var child = parentNode.firstChild; child; child = child.nextSibling) {
doSomething(child);
}完。
记得08年flashplayer10刚出来的时候,我看到过一个视频 ,Adobe的人就是用Textmate开发的Flash。 他是用Flex SDK里面的mxmlc命令编译的。当年我还用Mac OS的Automation功能做了一个应用程序,功能是把AS文件拖动到上面就自动给你编译成swf文件。 今年再回头看的时候,发现其实有更简单的方法。那就是用TextMate的Actionscript3 Bundle。然后我就尝试去配置这个环境,结果发现还很不容易。 下面把我的过程跟大家分享一下:
- 当然你要有一台Mac
- 你得先安装 TextMate。
- 安装上面提到过的actionscript 3 bundle。
- 到http://www.adobe.com/cfusion/entitlement/index.cfm?e=flex3sdk 下载flex sdk
- 将sdk解压,放到一个方便找到的位置
- 然后打开Textmate->prefrences->advanced->shell variables,添加一个PATH变量,值是你的flex sdk里bin的位置,比如:/Developer/SDK/flex_sdk_3.5/bin。 如果已经存在PATH变量,那么请不要改动原来的数据,在原来数据的后面加冒号(:),然后再加上bin目录的位置。
- 同样是在Shell Variables里面,添加一个LC_ALL变量,值是en_US.UTF-8。因为我发现flex会根据系统语言来显示错误信息,但是显示出来的是乱码,所以还是统一用英文的错误信息算了
- 打开flex sdk的目录,进入frameworks,编辑flex-config.xml,把<target-player>9xxx</target-player>替换成<target-player>10.0.0</target-player>
然后就爽把,新建一个as文件,写一些东西,然后按苹果键+B,就会自动调用mxmlc编译你的as文件,生成swf。 当然actionscript3 bundle的功能还有很多,自己去发觉吧~
2010年10月18日更新
今天搞了一下flash player的debug版本。可以把as3程序trace出来的信息记录到一个文件。步骤大概是这样的:
- 新建一个文件 ~/mm.cfg ,写上TraceOutputFileEnable=1
- 打开textmate的 Bundles >> Bundle Editor >> Edit Commands
- 打开左边的ActionScript 3 这个Bundle,然后点击左下角的+按钮,选择New Command
- 然后在右边的详细信息区域做如下选择:Save:Nothing, Input: None, Output: Show as HTML, Activation: Key Equivalent (然后点击右边区域,再按Apple键+D),Scope Selector: source.actionscript.3
-
在Command(s)区域,写:
echo "<pre>" cat ~/Library/Preferences/Macromedia/Flash\ Player/Logs/flashlog.txt echo "</pre>"
好了,现在在as3的代码界面按Apple+D就可以看到刚刚运行的flash的trace信息了。 上图:

运行效果如下:

完。
最近在搞flash的东西。遇到一个事件冒泡带来的问题。类似Javascript,flash里面的元素也有事件冒泡机制。经常会遇到给一个外层的元素绑定一个mouse_over事件,本意是想实现进入这个外层的元素的时候触发。但是实现的时候却有很奇怪的表现:鼠标在这个元素内部移动的时候,遇到这个元素北部的元素的时候,内部元素触发mouse_over,这个事件会冒泡到外层这个元素,同样触发外层元素的mouse_over事件! 然后你就会发现,鼠标在这个外层元素内部移动的时候,绑定的事件会被触发很多次。
为了解决这个问题,JQuery自己实现了mouseenter和mouseleave事件,实现鼠标移入这个元素内部和离开这个元素的时候触发,鼠标在元素内部移动不会触发。原理我就不详细说了,大概是判断触发的对象与绑定事件的对象之间的关系吧。
在flash里面我自己实现了一个类似的东西,实现了mouseenter和mouseleave的响应。 大概原理是利用mouseover和mouseout的关系(在元素内部移动鼠标到内部的其他元素上的时候,会先触发mouseout,然后触发mouseover事件),把mouseout事件延后处理,然后看如果mouseover到了内部元素上,那就取消mouseout执行,否则触发mouseleave。呃。。。貌似很混乱。。。。核心思想就是利用setTimeout(function(){ },0); 把函数延后执行。
as代码:
function mouseEnterLeave(red,enterCall,leaveCall)
{
red.mouseOvering = false; //标记鼠标是否处在元素内部
red.addEventListener(MouseEvent.MOUSE_OVER,function(e:MouseEvent)
{
var ct = e.currentTarget;
try{ clearTimeout(ct.outTimer); }catch(e:Error){ }
if (!ct.mouseOvering) //如果从外面到里面,那么触发mouseEnter
{
enterCall.call(red);
}
ct.mouseOvering = true;
});
red.addEventListener(MouseEvent.MOUSE_OUT,function(e:MouseEvent)
{
var ct = e.currentTarget;
ct.outTimer = setTimeout(function()
{ //把mouseout响应延后执行,如果能执行,那么表示鼠标移出
ct.mouseOvering = false;
leaveCall.call(red);
},0);
});
}
当然,这个函数也可以在Javascript中实现。就像这样:(非IE)
function mouseEnterLeave(o,enterCallback,leaveCallback)
{
o.addEventListener('mouseover',function(e)
{
try{ clearTimeout(o.outTimer); } catch(e) { }
if (!o.overing)
{
if (typeof enterCallback == 'function') enterCallback.call(o);
}
o.overing = true;
},false);
o.addEventListener('mouseout',function(e)
{
o.outTimer = setTimeout(function()
{
o.overing = false;
if (typeof leaveCallback == 'function') leaveCallback.call(o);
},0);
},false);
}
Javascript的例子见这里(非IE浏览器)。
完。


