从浏览器的focus blur说起

焦点事件 对于提高用户体验 无疑是有着 至关重要的作用... 但是使用他的同时 往往也会 给我们带来一些困扰...

 

先说个 借助焦点 解决一个 弹出菜单 的问题 的另类方案.

以往我们对于 弹出式菜单的 关闭 有以下几个方案.

 

1. 当鼠标离开当前div时 菜单即隐藏 或者 setTimeout 隐藏 如果 鼠标及时的回到菜单上 清除setTimeout的计时器.

 

2. 注册document.onclick事件的同时 禁止 div的click事件冒泡. 或者判断事件源是否是 div或div的子节点.

 

3.当菜单div显示时.在div的前面插入一个mask层使其覆盖整个浏览器客户区. 注册mask层的onclick 隐藏div即可

 

这些方法 各有利弊 方案1 用户体验稍差. 方案2 一旦 某元素 禁止了click事件的冒泡 则会失效 而方案3 显然 用户体验稍微有点问题 即mask层阻断了其他元素的 所有事件 这有时候是不能容忍的问题.

 

那么这里我们提出方案4 借助div.onblur 来弄... 但是一些问题也随之而来...

首先因为div默认不支持focus(非ie) 状态 所以更谈不上blur了. (ie虽然默认支持 但默认仍然不支持blur)

 

一个非常有趣的方法 是 给div 设置 tabIndex 一个自然数值 (当然不要超过Math.max 貌似和 z-index最大值 一样..没测试...偷懒一下) 即可让所有主流浏览器 的div 具备focus 和blur的能力. 这样我们就可以 给div.onblur=function(){this.style.display='none';} 就ok啦! 

但是事情远远没有完... 一个问题是 div一旦支持了focus 那么 当获取焦点后. 除了opera浏览器 以外其他主流浏览器 都会出现outline firefox和ie是虚线 safari chrome 是他们特殊的 发光效果的 蓝色或黄色的outline... 这显然是不能容忍的...幸运的是  除了ie6和ie7以外 甚至是ie8 我们都可以 div{outline:none;}  解决.

那么ie6和ie7要如何解决呢? 答案是  一个ie 特有的属性 --- hidefocus="true"  即我们只要 divElement.hidefocus=true; 搞定...

 

接下来 是这个方案的致命缺点了. 当用户按tab键时 就有可能 使div获取到焦点. (尤其是非ie浏览器 即使element.style.display='none' 也可以保持焦点.) 

或丢失焦点 这显然是我们不希望看到的....

解决办法 只有给document 注册 keydown了...(opera要注册keypress 原因是opera的keydown不是持续性事件.和其他浏览器有些区别.) 然后 通过当前document.activeElement (webkit引擎 貌似不支持...那么 只好去给 div前一个具备 tabIndex的元素 独立注册 onfocus 并使用一个 变量保存 其focus状态 或者可以考虑 所有浏览器统一这样做 全部放弃使用document.activeElement) 判断下一个是否会是我们的div 那么就让div的下一个 具备tabIndex的元素获取焦点就好了.

虽然看起来还不错 但最终我们可能还要遍历当前页中所有表单元素肯能存在的tabIndex值. 去自动判断div前一个 具备tabIndex的元素是否存在 并找到他..这样才能 把此方案 封装成控件  .  一个取巧的办法是 我建议 给divElement.tabIndex=Math.max; 这样我们就不再需要考虑 表单元素们是不是有tabIndex的值 或者 是否都是默认的0..而仅仅需要 这样:

var len,elem;

 document.forms[0] && (len=document.forms[0].length)>0 && 

( (elem=document.forms[0][len-1]).onfocus=function(e){... 维护this元素是否具备焦点的变量=ture;} );

 elem.onblur=function(e){. 维护this元素是否具备焦点的变量=false;};

然后 别忘了 判断 如果keyCode==9的情况下 我们的divElement是不是具备焦点 如果是 则 禁止浏览器默认行为 使tab更换焦点 失败.保持住我们的焦点.

 

到了这里 似乎 我们解决了问题 但是别忘了 anchor 元素等有些元素 仍然可以通过tab键获取焦点.那么 我们要还要处理所有的anchro <a>标签么? 显然这样不够好.  另一个不错的思路 是  当我们的 divElement.style.display=='block'时...我们再 设置其 tabIndex 当 'none' 时 删除掉该属性. 这个是最好办法不是么?

 

各种方案 有自己的有点 和缺点.. 也许你会从中找到 最适合自己需求的 解决方案.或找到更完美的办法... ok就倒这里吧.

 

这是一个典型的 借助focus blur 解决问题的例子...暂时就到这里 我们接着下个话题来说.

 

接着 我们说一个 focus blur的 优化. 这个方案是我的朋友wait和我提起的.. 我觉得很有价值 借花献佛的 拿出来说一下.

 假如 我当前页 表单中有 几十个 textbox 我想对他们 做 onfocus 和onblur 监听...  然后做一些事情

这时候我们 就是在没有必要 吧这几十个 都注册一下 而 仅仅给document 注册就好...但问题在于 focus 和blur 都是不冒泡的事件.没办法传到document上...

非ie的解决办法是  document.addEventListener 第三参数  true  .即 不采用冒泡 模式而改用 捕获模式.. 即让事件流 从document流回 事件源...这样我们注册到document上的 onfocus 就可以 监听到focus事件了...

而ie呢 不支持捕获模式... 但是 ie支持 两个特别的事件 focusin focusout  这两个事件对应 focus和blur  但是他们是支持 冒泡的....嘿嘿 到了这里 剩下的 就只是

函数内部判断事件源 是否是我们期望的东西 并写相应逻辑 即可.是不是 很好呢?

 

最后 在大多数浏览器中 一个有趣的现象是 element.onblur(){ 这里是不能在此让自己 this.focus()的....} 原因在于  当某个事件发生后 导致了 其他节点获取焦点的话 在这之前 就会发生element.blur 然后才会发生 otherElement.focus  所以在element.blur发生时设置focus() 并没有阻止  导致otherElement 获取焦点的事件...那么他仍然会在最后把焦点房到 otherElement上.... 解决办法 setTimeout...延迟处理...

 

焦点事件 对于window对象 也是很有意义的事情... 比如我们现在需要 dragAndDrop 功能时 当 drag时 我们切换窗口到别的 程序 并松开鼠标键时 再回到 网页..会发现 之前的mouseup 没有被捕捉  然后会发现被拖拽层 一直跟着鼠标跑...解决办法 就是 window.onblur=function(){obj.drop();} 

 

接着... 有时候我们不希望ie中 anchor元素 被点时 旁边出现讨厌的outline 边框.... 如果你知道这个边框出现的原因 同样是 因为<a> 获取到焦点的话 你一定就知道如何解决这个问题了....  你也会明白 为什么 有些站点 会使用  a{ outline:none;star:expression(this.onFocus=this.blur());} 来解决这个问题了吧? 其实就是 强制 anchor获取焦点后立刻 blur()而已...

 

 

 

posted @ 2010-04-28 02:57  Franky  阅读(6381)  评论(4编辑  收藏  举报