2010-07-27 by zhenn 发布在 html+css | 标签: 滤镜
近期项目中,发现这个诡异的问题,IE(以下如有特殊说明,指全部IE系列浏览器)下透明滤镜竟然会失效。起因在于,在做一个lightbox时,发现遮罩层透明效果是失效的,在第一时间里,我当然是把问题定位在YUI组建设计的缺陷上,但是当我用IE调试工具看到遮罩层渲染的样式时,我震惊了。如下图:

从渲染的css上看,没有任何问题,百思不得其解,更为让我疑惑的是,在我同事的机器上,遮罩层的透明效果是有的,难道我RP有问题?太不可思议了。
既然问题出了,就要去想办法解决,没有任何回避的理由,经过一番细心的排查,发现我和同事的机器唯一的差别就在于装的操作系统不一样,他的是XP,而我机器上装的是WIN7。这可能就是问题的所在了,但是细想,WIN7不可能不支持filter-alpha-opacity的,这样的结论相信没有人相信,因为我看到我以前写的遮罩层组件是可以正常工作的,所以排除WIN7不支持alpha的可能,那么问题到底出在哪里呢,请看可以正常工作的遮罩层CSS:

如果你足够细心,我想应该会发现区别在哪儿,没错,就是高度不一样,一个是20000px(失效),一个是1801px(有效)。既然高度会影响filter的渲染,那么肯定会有一个临界点,事实证明,的确如我所料,在WIN7的环境中,如果元素的高度超过4096px,就一定会导致filter-alpha-opacity失效,而这一切在XP环境中并不会发生,真是验证了一句话,在MS的世界里,没有什么不可能!
拓展:在vista系统中,也存在相同的问题,元素高度的临界点为2048px。所谓标准浏览器(firefox、chrome、safari之流)中,opacity渲染的透明度是跨系统的,不会存在失效问题
PS:邪恶的WIN7,邪恶的IE,永远遭我BS.
2 条评论 »
2010-07-05 by zhenn 发布在 javascript | 标签: ajax, 选项卡
关于ajax异步请求的tab技术很常见,在很多门户网站上都可以看到他的身影,特别是在频道首页,这样做的目的是显而易见的,分步请求数据,减缓服务器的压力,带来更为流畅的用户体验。如下图:

设计起来当然不是什么问题,就是一个特殊的和服务器有通信的tab而已,在首轮tab切换过程中,发送一个XHR,然后再把Server响应的数据(基本上是html字串)塞到相应的容器中即可。这个过程中有个问题需要注意,在每个选项点击第一次之后,已经把所需要的数据加载到客户端,那么再次切换的时候就没有必要重新请求服务器了,这样做的依据就是判断内容容器中的innerHTML是否为空。
诚然,你如果真的反其道而行之,也并无过错,并不会影响网页功能的展现,但却是徒劳无益,反而加重服务器的负担,并且会影响到用户体验,这也就有被于使用ajax tab的初衷了。
同样的,详情请登录 :Demo
没有评论 »
2010-07-03 by zhenn 发布在 javascript | 标签: 基础知识, 浮点数
老实说在今天之前,我对javascript的浮点数运算毫无认识,觉得应该和实际运算中一样,然而,有些事情往往会出人意料。
先看一组数据:
0.1 + 0.11 = 0.21000000000000002
0.2 * 3 = 0.6000000000000001
0.7 / 10 = 0.06999999999999999
你相信吗?如果不信,那么立即打开firefox浏览器测试吧。有一点可以确信,你看到测试结果的表情,一定先是惊讶,后来是迷惑。
这里有几个demo,再来看看出现浮点数运算bug的概率,猛击之:加法、乘法、除法
对于这样的结果,我不知道是怎么产生的,但是既然是bug,就一定是可以hack的,思路很简单,先把所有的运算数转成int型(表达不准确,javascript的没有int、float之分),计算出两个运算数的小数点后的位数,再进行适当的运算,就可以得到原始的我们期望看到的结果了。代码如下:
加法运算:
- function add(num1,num2){
- var reg = /\./i;
- if(!reg.test(num1) && !reg.test(num2)){
- return num1 * num2;
- }
- var r1 = 0, r2 = 0, m;
- var str1 = num1.toString(), str2 = num2.toString();
- if(str1.indexOf('.')>-1){
- r1 = str1.split('.')[1].length;
- }
- if(str2.indexOf('.')>-1){
- r2 = str2.split('.')[1].length;
- }
- m = Math.pow(10,Math.max(r1,r2));
- return (mul(num1,m) + mul(num2,m)) / m;
- }
乘法运算:
- function mul(num1,num2){
- var reg = /\./i;
- if(!reg.test(num1) && !reg.test(num2)){
- return num1 * num2;
- }
- var len = 0, str1 = num1.toString(), str2 = num2.toString();
- if(str1.indexOf('.')>=0){
- len += str1.split('.')[1].length;
- }
- if(str2.indexOf('.')>=0){
- len += str2.split('.')[1].length;
- }
- return Number(str1.replace('.','')) * Number(str2.replace('.','')) / Math.pow(10,len);
- }
除法运算:
- function div(num1,num2){
- var reg = /\./i;
- if(!reg.test(num1) && !reg.test(num2)){
- return num1 * num2;
- }
- var len1 = 0, len2 = 0;
- var str1 = num1.toString(), str2 = num2.toString();
- //计算位数差
- if(str1.indexOf('.')>-1){
- len1 = str1.split('.')[1].length;
- }
- if(str2.indexOf('.')>-1){
- len2 = str2.split('.')[1].length;
- }
- return mul(Number(str1.replace('.','')) / Number(str2.replace('.','')),Math.pow(10,len2-len1));
- }
没有评论 »
2010-06-26 by zhenn 发布在 javascript | 标签: 倒计时
近期在做排列五的彩票项目,每一期都有购彩时段,即用户打开这个排列五的页面时,会从服务器传来一个remaintime(离本次彩期结束的剩余时间),然后这个时间在客户端递减呈现给用户看,让用户获得本次彩期的剩余时间。
实现原理挺简单的,在此不在赘述,敬请猛击demo。
调用方法如下:
- new TheTime({
- //容器id
- container: 'remaintime',
- //服务器返回的剩余时间,单位为秒
- remaintime: 10000,
- //倒计时完成时的回调
- callback: function(){
- document.getElementById('remaintime').innerHTML = '已截止';
- }
- });
没有评论 »
2010-05-15 by zhenn 发布在 web开发相关 | 标签: YUI
没有评论 »
2010-04-30 by zhenn 发布在 javascript | 标签: 事件冒泡, 捕获
对“捕获”和“冒泡”这两个概念,我想我们对冒泡更熟悉一些,因为在我们使用的所有浏览器中,都支持事件冒泡,即事件由子元素向祖先元素传播的,就像气泡从水底向水面上浮一样。而在像firefox,chrome,safari这类所谓的标准浏览器中,事件传播还有个阶段,那就是捕获阶段,这个很少有用武之地,所以被人疏忽遗忘也在所难免了,不常用不代表它不存在,本着科学严谨的态度,我们有必要去看一下它的庐山真面目。
事实上,捕获阶段是一个和冒泡阶段完全相反的过程,即事件由祖先元素向子元素传播,和一个石子儿从水面向水底下沉一样,要说明的是在IE,opera浏览器中,是不存在这个阶段的。从各浏览器提供的注册事件监听的方法中可见一斑,例如适用于ie,opera的attachEvent,有两个参数,attachEvent(”on”+type,fn),而适用于所谓标准浏览器的addEventListener则有三个参数,addEventListener(type,fn,boolean),前面两个参数不用解释,第三个参数boolean,就是决定注册事件发生在捕获阶段还是冒泡阶段,具体参考如下:
话说,眼见为实,耳听为虚!那么好吧,请看demo,猛击这里
在这个demo中,注册事件监听默认发生在冒泡阶段,由于“元素a”,“元素b”和“元素c”有嵌套关系,为了更好的表现冒泡过程,所以请先点击“元素c”,会看到在各个浏览器中呈现的视觉效果是一致的,即事件是从c向a传播的,这也恰恰证明了所有浏览器都有冒泡这个过程的观点。经过上述的介绍,唯一让你感到吃惊的应该是,从视觉效果上,“元素c”并没有包含在“元素a”和“元素b”里面,而表现出来的效果确实证明”click”事件依次从c经过b传播到a的,反而包含“元素c”的“元素d”却没有响应click事件。事实上,这并不奇怪,因为事件冒泡和视觉上的布局结构是毫无关系的,这个冒泡过程仅仅依赖于dom元素的html结构(即嵌套关系)。效果见下图:

提及事件冒泡,那么不得不提“阻止事件冒泡”这个概念,因为我们最经常处理的任务就在于如何阻止事件的冒泡,来避免一些不必要的麻烦,关于这点,由于在之前的文章中单独讨论过,在此不再赘述,详见:阻止事件冒泡
言归正传,为了更好的看到捕获和冒泡的区别,那么这时候需要你把这个demo另存到本地,然后找到如下图所示的代码片段:

接下来,你要做的就是,把这个代码片段中false改为true,也就是说让注册事件监听在捕获阶段进行,依次ctrl+s,在各个浏览器中打开这个页面,细心的你会发现,在firefox,chrome,safari中表现效果发生了变化(IE,opera中无变化,因为不存在捕获阶段),如下图:

对比两张图片所显示的视觉效果,应该足以证明本文开头所提出的观点了,如果针对事件捕获和冒泡尚有任何疑问或不解,请留言或者Email我,交流研究!
2 条评论 »
2010-04-29 by zhenn 发布在 javascript | 标签: 事件机制
给dom元素绑定事件监听,很easy,即使刚入门的javascript编程人员都可以办到,比如obj.onclick = fn就可以很轻松的监听obj的单击事件,这固然是没有问题的,但是问题就在于在这个监听过程中,很难触发N个代码片段,例如:
- obj.onclick = fn1;
- obj.onclick = fn2;
- ...
事实上,如果这么做,fn2会把fn1覆盖,也就是说在单击obj时只会执行fn2中的代码片段,fn1则会被忽略,很显然这样不能满足我们的需求。在考虑到这个问题的时候,JS已经为我们准备了像attachEvnet、addEventListener这样的方法来满足我们的需求,虽然在不同级别的浏览器中方法不尽相同,利用对browser的能力检测还是可以轻松的实现一个兼容的方法:
- function bindEvent(elem,type,fn){
- if(elem.attachEvent){
- elem.attachEvent("on"+type,fn);
- }else{
- elem.addEventListener(type,fn,false);
- }
- }
- //调用
- bindEvent(obj,"click",fn1);
- bindEvent(obj,"click",fn2);
- bindEvent(obj,"mouseover",function(){alert(this)}) //this->window
这样目的就达到了,当单击obj时,依次执行fn1、fn2。解决了这个问题后,细心的淫们会发现在执行的function中this执行是有问题的,始终指向window,又不是一个不小的麻烦。那么有没有办法,来解决这个棘手的问题呢,答案是肯定的,这正是我写这篇文章的意义所在,废话!
其实只需对bindEvent稍作加工即可,如下:
- function bindEvent(elem,type,fn){
- if(elem.attachEvent){
- elem.attachEvent("on"+type,function(){
- fn.apply(elem,arguments);
- });
- }else{
- elem.addEventListener(type,fn,false);
- }
- }
- bindEvent(obj,"mouseover",function(){alert(this)}) //this->elem
理论上,这样的解决方案近乎完美了,但是还有一个问题亟待解决,因为在attachEvent中使用了匿名函数来执行fn,导致在无法使用detachEvent来删除绑定的某个代码片段,也许obj["on"+type] = null可以解决,清空绑定的代码片段,这很粗暴邪恶,同时也违背我们的业务需求。
我想要从根本上解决这些问题,就一定抛弃attachEvent这个方法,事实上我也是这样做的,如下:
- //绑定事件
- function bindEvent(elem,type,fn){
- if(elem.attachEvent){
- var typeRef = "_" + type;
- if(!elem[typeRef]){
- elem[typeRef] = [];
- }
- for(var i in elem[typeRef]){
- if(elem[typeRef][i] == fn){
- return;
- }
- }
- elem[typeRef].push(fn);
- elem["on"+type] = function(){
- for(var i in this[typeRef]){
- this[typeRef][i].apply(this,arguments);
- }
- }
- }else{
- elem.addEventListener(type,fn,false);
- }
- }
-
- //移除事件绑定
- function removeEvent(elem,type,fn){
- if(elem.detachEvent){
- if(elem["_"+type]){
- for(var i in elem["_"+type]){
- if(elem["_"+type][i] == fn){
- elem["_"+type].splice(i,1);
- break;
- }
- }
- }
- }else{
- elem.removeEventListener(type,fn,false);
- }
- }
这段代码有点饶人,首先根据type类型给elem注册一个相关的属性_type,来存放elem同种类型事件要执行的代码片段,当然在这个过程中要先判断这个属性是否已经存在,如果存在,则跳过这一步。然后要遍历这个存放代码片段的数组,判断是否有和要添加的代码片段相同的代码片段,如果有,直接跳出function。假设在上一步条件不满足的情况下(即代码片段不重复),那么把这个代码片段push到相应的数组中。之后干的事情,我想大家应该很清楚了,没错,就是把相应数组里的代码片段依次apply到elem下执行。当然,这个过程只有在触发事件的时候,才会发生。
依照这个原理,那么要删除绑定的某个代码片段就很容易做到了,只需要从elem["_"+type]这个数组中把相应的代码片段删除即可。
解释一下为什么这个存放需要绑定代码片段的数组设置为elem的一个属性,其实理由很简单,就是不用声明一个全部变量(数组类型),来管理这个二维的数据结构,这样可以保证bindEvent,removeEvent的高度独立性。
最后,希望各位大淫能帮忙测试一下这两个方法的健壮性如何,是否还存在一些bug,如果有,提交给我,zhenn不胜感激之至!
2 条评论 »
2010-04-24 by zhenn 发布在 javascript | 标签: 基础知识
对于&&和||运算符,相信大家都用过,分别进行逻辑与运算和逻辑或运算符。如果&&的第一个运算数是false,则不再考虑第二个运算数,直接返回false。如果||的第一个运算数是true,那么也不再考虑第二个运算数。
没有实践就没有发言权,demo如下:
- var a = true, b = false;
- function test(param){
- alert(param);
- }
- a && test(1);
- b && test(2);
- a || test(3);
- b || test(4);
执行如上代码,事实上只执行了test(1)和test(4),原理如上所述。
a&&test(1)这种运算,其实我不知道是否有个专业的名词去描述它,在javascript森林群ToFishes同学称之为“惰性运算”,那么暂且叫它“惰性运算”吧,实际上相当于if(a){test(1)}。
其中||运算符,有个很重要的应用就是给function的参数设置默认值,如:
- function test(param){
- param = {
- name: param.name || "zhenn",
- age: param.age || 25
- }
- alert(param.name + " is " + param.age + " years old.")
- }
- test({});
以上内容,针对刚入门javascript的童鞋们,大湿级的淫们可以直接skip.
5 条评论 »
2010-03-31 by zhenn 发布在 javascript | 标签: 值, 地址
一个陷阱,引发一次深刻的思考,代码如下:
- var a = 11111;
- var b = {
- pro: "test"
- }
- function fn(a,b){
- a = 2222;
- b.pro = "zhenn";
- }
- fn(a,b);
- console.log(a); // 11111
- console.log(b); // b.pro = "zhenn"
在JavaScript中,除了Object,Array是地址引用,其他数据类型,诸如Number
、Boolean、String等都是值引用,这就是从陷阱中挖出的东西,同一个坑不能跳进去两次啊!杯具…
3 条评论 »
2010-03-31 by zhenn 发布在 javascript | 标签: delete
delete这个操作符呢,在javascript中不是很常用,但是他的特性的确很诡异。
1,删除对象的属性,代码:
- var o = {
- a: 1,
- b: 2
- };
- delete o.a;
- alert(o.a); //undefined
那么到底delete删除的是对象的属性还是对象的属性值呢,我开始觉得删除的应该是值,因为结果是undefined,而没有报错。但是事实上,我的看法是错误的,举例说明:
- var o = {};
- var a = {
- pro: "zhenn"
- };
- o.c = a;
- delete o.c; //删除对象o的属性a
- console.log(o.c); // undefined
- console.log(a.pro); // zhenn
通过上述代码,不难看出在delete o.c之后呢,并没有删除o.c所指向的值,也就是对象a依然存在,否则a.pro应该是过不了编译这关的。说到这里,可以这么理解delete删除对象的属性,其实相当于删除了对对象中属性值的引用而已,但是这个值依然在对象栈中!
2,对数组的操作,先看代码:
- var arr = [1,2,3];
- delete arr[2];
- console.log(arr.length); // 3
- console.log(arr); // [1,2,undefined]
又一次证明了,delete并没有真正删除元素,只是删除了元素所对应的键值。为了更进一步认清delete的本质,和Array中的pop方法比较一下。如下:
- var arr = [1,2,3];
- arr.pop();
- console.log(arr); // [1,2]
- console.log(arr.length) // 2
这下应该真相大白了。
3,以上对对象和数组的操作,还很好理解,但是对于变量的操作,难免让人琢磨不透,代码如下:
- var a = 1;
- delete a;
- alert(a); // 1
-
- function fn(){ return 42; }
- delete fn;
- alert(fn()); // 42
-
- b = 2;
- delete b;
- alert(b); // b is not defined;
很难解释通啊,同样是全局变量,用var声明的竟然删除不了,而直接声明的变量b竟然可以删除,不能不说delete很诡异了,在ECMA给出的解释中,也仅仅是说通过var声明的变量和通过function声明的函数拥有DontDelete特性,无法被删除。
一条评论 »