Technology


Apr13

opera下的onload、onunload事件无法正确处理?

今天,QA在测试JS框架的时候发现了一个问题:opera下不能绑定onunload事件,最新的opera 10.51也不行。 网上的解释千奇百怪,有说opera不支持unload事件的,有说使用beforeunload事件代替unload事件,还有的人说把通过js修改opera的history.navigationMode为’compatible’后就能正常支持这个事件了。 而按照我的测试,不管是按照window.unload绑定还是用addEventListener绑定的事件都无法在刷新、前进、后退,页面关闭,浏览器关闭时被执行。beforeunload事件表现相同。正在我怀疑opera彻底不支持unload事件时,恰好找到了bytes.com上的这篇文章。上面说的很清楚: There is no solution because according to Opera developers and users this is not a bug. Opera maintains the runstate of scripts, so it’s as if we never left the page when we return back to it. According to Opera, onload should only fire when loading the page for the first […]

trackback Tags: 评论 (2)

Apr2

博客迁移到nginx

Nginx是一个高性能的HTTP服务器,也是一个 IMAP/POP3/SMTP 代理服务器。 Nginx 是由 Igor Sysoev 为俄罗斯访问量第二的 Rambler.ru 站点开发的。nginx采用epoll模型来进行网络IO,效率比apache的select模型高出很多。 一年前,服务器就使用Nginx做为最前端的webserver,主要是用来做静态文件的缓存,而php还是通过apache在运行。有文为证。 打算在近期逐步脱离apache,完全使用nginx,上周末参照张宴的文章搭建好了环境。 今天花了一点时间,把整个撞击思想博客的后端改成了Nginx+php-fpm,过程还算顺利。配置文件参考了wordpress mu论坛中的这篇帖子。 By the way,最近一周使用tornado+mongodb开发了部门内部的一个小系统,感觉好极了,一路编码很顺畅,学习成本也很低。而由于NOSQL的存在,数据库也可以很方便的应对变化。 嗯,我手里有多了几把锤子。

trackback Tags: 评论 (2)

Mar12

傲游、世界之窗下的getBoundingClientRect问题

问题 今天,同事爆料suggeestion在傲游、世界之窗下,使用滚轮缩放后,suggestion的框和input不能对齐,会出现类似下面的效果(示意图): 页面是quirk模式,如果换用standard模式,就不会有问题。 追踪 追查发现,发现计算input位置一个库函数得到的结果不准确。对比yui和jquery的代码后,发现处理逻辑完全一致:如果是IE系列,直接使用getBoundingClientRect得到元素的top和left,之后再看是否为qurik模式,决定是否要减去两个像素。 问题就很明确了,是getBoundingClientRect()返回值与实际值不一致。此外,发现一个现象,如果放大屏幕,那结果偏小,缩小屏幕,结果偏大。于是尝试使用getBoundingClientRect()的返回值除以放大比率,果然这样就匹配了。 解决 首先,建议页面全部使用standard模式。 由于不知道如何使用傲游或者世界之窗的方法来直接获得缩放比率,同时担心在不同的IE外壳下会有兼容性问题,于是写了下面这个函数,很trick,实在不行的时候可以考虑用用: function getIEShellZoomRate (){ var div = document.createElement(“DIV”); div.style.position = “absolute”; div.style.top = “100px”; document.body.appendChild(div); var divBox = (div.getBoundingClientRect()).top; if(!browser.isStrict){ //fix 2px in IE while in quirksmode divBox -= 2; } document.body.removeChild(div); return parseFloat(divBox/100); } 其他 初步判断,原因应该是getBoundingClientRect()返回值是真实肉眼看到的像素值,而如果把这个像素值设置到元素的top和left属性后,实际值会随着缩放效果放大或缩小,造成两者不一致。 此外,在傲游中,页面加载完毕后,需要闪烁一下才能到缩放后的效果,傲游可能是在获得IE的渲染结果以后,再自行放大,如果使用了standard模式,重新绘制的区域是没有缩放效果的。 IE外壳的确方便了最终用户使用浏览器,但是也无形中让用户更依赖一个本来就很破旧的浏览器,不愿意升级到浏览器的更新版本。 珍爱生命,远离IE;拒绝毒品,抵抗浏览器外壳。

trackback Tags: 评论

Dec29

添加第一个功能按钮 – 在线编辑器(3)

在实现了在网页中插入一个可编辑区域以后,用户已经可以在这个区域中操作富文本内容。但是做为一个真正的编辑器是要有工具栏的,用户能通过工具栏上的按钮方便的对选中的文本进行操作。本文将介绍如何添加编辑器功能按钮。 加粗按钮 首先,我们在页面中添加工具条的HTML代码: <div id=”editor_toolbar”>     <input id=”editor_toolbar_bold” type=”button” value=”B” style=”font-weight:bold;” onclick=”editorInstance.bold()” /> </div> 工具条中包含一个按钮,在按钮被按下时,调用editorInstance的bold()方法。期望的结果是:在编辑框内,选中的区域将被加粗。 加粗操作 接下来需要实现加粗功能,在此之前,需要对execCommand()有一定了解 execCommand execCommand浏览器自带的一个接口,通过它,我们可以对document、selection、或者一个给定的range进行操作。如加粗、斜体等。 语法: bSuccess = object.execCommand(sCommand [, bUserInterface] [, vValue])   sCommand String,指定需要执行的命令,如Bold   bUserInterface Boolean,可选,决定是否显示用户界面,如弹出一个提示框让用户输入,默认为false。   vValue Variant,可选,一个字符串、数字、或者其他需要assign的值 一般来说,第二个参数会传入false,因为编辑器本身会接管用户界面,第三个参数按照实际需要传入。这样就可以实现对选区的加粗: document.execCommand("Bold", false, false); 代码实现 只需调用浏览器的execCommand方法,选区就被加粗了。 editor.Editor.prototype.bold = function(){ this.document.execCommand("Bold",false,false); }; 简单极了!但是总觉得少了什么?是的,没有状态反射。不论用户选中的是否一段加粗的文本,按钮只有一个状态,用户无法感知当前选中文本的状态。 状态反射 添加状态反射的思路是:在选区发生变化的时候,编辑器核心派发一个事件,在此事件发生时判断此时选区的状态,根据状态设置按钮样式。 为何不由编辑器核心直接设置按钮样式呢?这是因为通过事件通讯能最大程度的降低模块和核心之间的耦合,也是模块可插拔的基础。 监听选区变化 造成选区发生变化的根源是鼠标点击或者键盘击键。因此,通过监听onkeydown和onmouseup事件就可以较粗略的得知选区变化。其实,此处将成为编辑器复杂后的第一个性能瓶颈,具体优化办法今后将具体描述。 事件机制 […]

trackback Tags: 评论

Dec23

历史、现状和目标 – 在线编辑器(1)

关于本系列文章 计划暂时是这样,首先介绍一些历史、知识背景,然后详细分析现状和需求,开始设计一个可扩展,低耦合的编辑器,最后针对一些难点问题进行单个分析。 历史、现状 起初,世界上的浏览器本没有编辑器功能。到了IE4.0 (1997年),微软提供了document.selection,利用这个对象,前端开发人员可以让一个iframe进入编辑状态,里面可以输入富文本内容,并且能通过程序添加复杂的编辑操作。那个时候还没有W3C的相关规范,这个selection对象以及两种选区类型(textRange和controlRange)都是微软自己实现的。 三年后,有了W3C的相关规范(2000年),这个规范和IE的实现完全不同,firefox等一干浏览器陆续根据规范实现了自己的selection和Range对象。 前端工程师的悲剧就这样酿成了,有两套截然不同的方式来处理选区(selection),不仅仅如此,对execCommand()方法的支持也大相径庭,一个相同的命令在不同的浏览器下执行会出现不同的效果。最要命的,浏览器还经常会有一些诡异的问题,比如在某些时候对选区的不当操作会导致IE直接崩溃等。 但是在线编辑器的使用场景相当广泛:写blog、论坛发帖、淘宝卖东西、在线聊天等应用场景都需要使用编辑器,他们对编辑器的要求各不相同。编辑器已经成为了一个基础件,几乎所有的互联网公司都需要用到。 后来出现了一些开源编辑器,比较出名的有:ckeditor(原来叫fckeditor), tinymce,eWebEditor等,最近,淘宝也开源了他们的kissy编辑器(功能尚不完善)。但是开源编辑器都各有不足,ckeditor足够强大,重写了浏览器的所有操作。但是模块之间的耦合性很高(3.0有所改善,但仍然不够),体积过于庞大,其他的一些编辑器则要么bug太多,功能不全,要么不方便二次开发。另外,一些对编辑器要求很高的服务,像zoho、Google docs等等,都带有一个很强大的编辑器。 目标 一个优秀的编辑器,首先要尽量解决浏览器之间的差异,在不同的浏览器下表现一致。 另外,为了让编辑器在各种不同环境下适应不同的需求,尤其是二次开发的需求,这个编辑器还应该是“通用”、“可扩展”、“低耦合”的。 具体的设计思路在后面我会专门撰文说明,首先的这几篇文章,是让一些没有接触过编辑器开发的童鞋了解一些基础知识。 本文是在线编辑器系列文章的第一篇,在这个系列文章中,我会逐步深入和大家分享编辑器开发过程中的奥妙和各种奇怪的问题。下一篇文章将介绍如何拥有一个可编辑区域,如果你对我的文章感兴趣,欢迎从页面右边订阅我的博客及时收到更新。

trackback Tags: 评论 (1)

Dec13

善用分布式版本管理系统管理个人代码

大多数人都知道或者善用多种版本管理系统,传统集中式版本管理系统有CVS、SVN等,后来,出现了一些分布式管理系统,如GIT、mercurial等。几乎所有有开发工作的公司,都会选择一个版本管理系统,并且制定一些规范来管理代码,道理很简单,使用版本管理系统能在多人开发,跟进bug,追查问题方面减少很多管理和沟通的成本。 但是,我觉得个人的代码,包括一些软件配置文件,完全可以使用分布式版本管理系统来管理,可以让我们减轻很多麻烦。 使用传统的集中式版本控制软件来管理,好处如下: 不用担心因为无意的错误修改,删除而丢失过去的代码 可以随时查看历史情况,你就不用为“xx功能、yy修改到底是什么时候加进来的,为什么要加”这类事情而挠破头皮了 可以进行分支开发,比如说一个功能你不是很确定能否实现,拉一个分支开发就是了 更进一步,使用分布式的版本控制还有如下好处: 在本地就有版本仓库,你可以在任何时候进行ci和roolback,无需联网 超级快速的分支切换 方便的和他人(其他机器)共享代码,一个push,或者pull,就能快速的同步修改 下面我举几个例子来让大家感受一下善用分布式版本管理的好处。 保证多台机器上的配置文件统一 我们有很多很多配置文件,尤其是linux下。vim配置,apache站点设置,桌面的配置,pidgin的账号设置等等。 但是如果有多台计算机,如果两台机器上配置不统一,用起来不顺手,更可能会带来麻烦,我们需要找到这些配置文件,并且拷贝一份最新的到当前机器,但是如果两边都有修改,就可能会丢失修改,或者需要手动merge。 过去可能用人使用过rsync来同步文件,能解决部分问题,但是如果使用分布式版本管理系统,在每台机器上都有一个独立的代码仓库,用这来管理配置文件再方便不过了! 首先,我们现在一台拥有最新版本配置文件的机器上初始化一个版本库 cd ~/ hg init hg add .vimrc .screenrc .fvwm #添加所有你关注的配置文件 然后,一旦有新机器需要使用这个配置,clone一份即可 cd ~/ hg clone ssh://berg@lastPC://home/berg/ hg up 如果在任何一台机器上有修改,只需要ci到当前机器,并且到其他机器上pull这份修改。 hg ci ssh berg@anotherPC://home/berg/ 我们不需要关心代码的合并,也不需要关心配置文件的具体名称和位置(在linux下面,所有的配置都是隐藏文件),一切细节都交给mercurial来管理。 使用中心服务器 分布式管理原则上是没有一个强制的中心服务器,但是如果我们在管理的规则中加上中心服务器,在某些时候就能避免一些尴尬的发生。 举一个例子,假设我有三个开发环境,一台在公司,一台在家里,还有一台服务器。在公司时,我家里的机器是离线的,在家里则反过来。于是我白天在公司开发完毕后,想在家里获得最新的版本,最简单的办法就是在服务器上架设一个中心版本库,下班时,push代码到服务器上,回家就可以直接pull了。 使用中心服务器可以避免在需要开发时,发现最新版本所在计算机不在身边,并且处于离线状态的尴尬。 灵活的分支开发 不要以为分支功能在小项目中就毫无用处,有的时候,善用分支可以减少很多麻烦。而集中的版本控制系统处理分支很麻烦,让人避退三舍,可是到了分布式版本控制这里,分支变得很简单,看看下面的例子。 我在开发一个项目时,遇到了一个较大的修改,涉及面较广。 如果直接在主干上开发,如果在开发过程中发现了bug,或者其他临时插入的想法,我有两种选择: 直接在线上修改,或者重新clone一份到本地的其他目录进行开发 把“projectA”这个项目做为一个分支在本地开发 两种好处的优劣是很明显的,第一种,我要么需要冒线上修改的风险,要么需要在本地配置另外一套环境;而第二种,我只需要在碰到其他问题的时候,将分支切换回去就好了。下面是命令示例: 首先,将本地仓库的当前branch设置成项目A的名称:projectA hg branch projectA […]

trackback Tags: 评论 (1)

Dec5

在javascript编程中灵活使用try-catch

上周,在分享家的qq交流群里,经常有人向我反馈不能下载东西。于是直接联系了一个用户,发现他的浏览器报出pageTracker对象不存在错误。 问题很显然了,由于我在下载链接的onmousedown事件中添加了Event统计,如果用户的Google Analytics代码没有加载成功(显然是万恶的教育网造成的),那么就会报出这样错误。 请看原来的代码片段: function analytics(category, action, label, value){   pageTracker._trackEvent(category, action, label, value);   } 修改一下,加上try-catch屏蔽这个错误: function analytics(category, action, label, value){       try{           pageTracker._trackEvent(category, action, label, value);       }catch(e){}   } 用try-catch来屏蔽浏览器错误是一个Javascript编程中很常用的一个技巧,灵活使用可以给开发者减少很多麻烦,下面再举一例。 最近,我在开发一个通用的所见即所得编辑器。某些时候,程序需要记录住当前状态的节点,给后续程序处理。但麻烦来了,用户如果持续的在进行编辑动作,在程序后续处理时,节点可能已经不在DOM树中了。 在IE下,如果你调用一个不存在的文本节点(nodeType == 3)的任何属性或者方法,浏览器直接报错。根本找不到能判断这个节点是否存于DOM树中的方法。于是有了如下代码: try{     if(ele.ownerDocument != editor.document) return false; }catch(e){ […]

trackback Tags: 评论

Jun19

offsetwidth、clientWidth、scrollWidth的区别

最近在写一个拖拽模块,支持range,也就是说一个element只能在一个固定的范围内拖动。于是我需要知道当前被拖动的element的大小。 最初我使用scrollWidth,发现在ie6下,如果这个element有border,就会有偏差,后来经过erik的建议使用offsetWidth解决。 上网查了一下,得到如下答案: scrollWidth是对象的实际内容的宽,不包边线宽度,会随对象中内容的多少改变(内容多了可能会改变对象的实际宽度)。 clientWidth是对象可见的宽度,不包滚动条等边线,会随窗口的显示大小改变。 offsetWidth是对象的可见宽度,包滚动条等边线,会随窗口的显示大小改变。 这篇日志是每日一web-dev tip计划的一部分,如果你关注web开发或者linux,请订阅我的rss。

trackback Tags: 评论

Jun18

chrome下的私有关键字问题

前几天,一个同事报出搜藏在chrome下右侧内容无法显示,经过leeight的追查,发现是因为在页面中定义了一个函数: function top(){ window.scrollTo(0,0); } 结果在其他所有浏览器下,typeof top都是function,唯独在chrome下是obejct。 怀疑在chrome的v8引擎下,top是一个内置的对象,而这个对象是不能被编程覆盖导致的。 最终的修改办法当然就是换一个函数名了,这个bug也让我更加认识到一定不要使用太过常见的单词做变量或者方法名,就算代码整个是一个人维护的,浏览器或者语言本身也是会和你过不去的…… 这篇日志是每日一web-dev tip计划的一部分,如果你关注web开发或者linux,请订阅我的rss。

trackback Tags: 评论 (1)

Jun11

sphinx中增加索引碰到的问题

今天调整了一下分享家的搜索系统,分享家的搜索是用sphinx做的,之前只索引了文章表,现在想多索引几个表,于是blahblah两下改了配置文件。 然后运行: /usr/local/bin/indexer xxx –rotate 成功完成,但是发现搜索的时候没有效果,往下面追查,发现没有查找这个索引,继续追查,发现所有的新索引都是xxx.new.sph。 上sphinx论坛找到这样一个帖子。最后一个回复人说的很明白了: I suppose that you added the second one but did not restart searchd since. At the moment it is only able to pick up new index data on SIGHUP, but not new index definitions from sphinx.conf Restarting searchd should help. 事情就简单了,重新跑索引(不要–rorate),kill searchd,然后重启就行了。 这篇日志是每日一web-dev tip计划的一部分,如果你关注web开发或者linux,请订阅我的rss。

trackback Tags: 评论