4.5扩展TabBox事件
在第二章中,我们已经实现了TabBox组件,即leftMenu类。在那里已经实现nodeClick事件。这个事件的实现就应用了4.4节中的组件事件的编写。这一节我们通过扩展其树的节点的导航来综合应用本章中讲到知识点。
4.5.1使Tree导航能正常运行
Tree组件本身就实现了方向键导航,但该导航实现对于我们的TabBox来说是有点问题的,我们可以先运行第二章的例子看看其上下方向键导航,在IE中,当它其Tab折叠之后展开,其方向键导航失效。在FireFox中其并不失效,这个问题是我们要解决的。
我们先来思考一下键盘按键的事件源(srcElement),也就是说键盘按键事件是由谁来处理?鼠标点击时,它会根据点击坐标点找到页面对应dom元素,如果它注册了监听函数就进行处理,没有就冒泡给其父节点。而键盘按键事件如何找到其对应的dom元素呢?
于是在浏览器出现了焦点(focus)概念,用来指定某个dom元素处在激活状态中。该元素会接收所有键盘事件并根据其注册监听函数进行处理,如果没有监听函数,就会向父节点冒泡。一般来讲如果鼠标点击的元素都能获得焦点,这也就是为什么文本框输入来先采用鼠标把光标定位在文本框元素上。如果页面没有焦点,其接收键盘事件的就是body元素。在开发注册键盘事件时,有时在其元素上不能响应,然后在body元素上却能响应就是因为其没有获得focus焦点。
对于浏览器不能自动去设定指定元素的焦点,我们可以通过手动调用focus函数来为其元素获得焦点,除去焦点则可以通过blur来手动完成。我们TabBox的问题也出现在这里,每次选中Tree节点时,都会调用TreeNodeUI中的focus为其元素集焦。我们看一下其代码:
focus : function(){

if(!this.node.preventHScroll)...{
try...{this.anchor.focus();}catch(e){}}

else if(!Ext.isIE)...{

try...{ var noscroll = this.node.getOwnerTree().getTreeEl().dom;
var l = noscroll.scrollLeft;
this.anchor.focus();
noscroll.scrollLeft = l;
}catch(e)...{}}
},
initTabsel : function() {

for (var i = 0;i < this.items.length; i++) ...{

Ext.TabEvent = Ext.extend(Ext.tree.DefaultSelectionModel, ...{

init : function(tree) ...{
this.tree = tree;
Ext.getBody().on("keydown", this.onKeyDown, this);
tree.on("click", this.onNodeClick, this);}
});
t.selModel=new Ext.TabEvent();
}},
initEvents : function() {
Morik.Office.LeftMenu.superclass.initEvents.call(this);
this.initTabsel();
this.initTreeEvent();
},
selectNext : function(){
var s = this.selNode || this.lastSelNode;


if(!s)...{ return null; } ①
if(s.firstChild&&s.isExpanded())...{return this.select(s.firstChild);}②
else if(s.nextSibling)...{ return this.select(s.nextSibling); } ③

else if(s.parentNode)...{
var newS = null;

s.parentNode.bubble(function()...{ ④

if(this.nextSibling)...{
newS = this.getOwnerTree().selModel.select(this.nextSibling);
return false; }
else...{ //这是要扩展的地方 } }
return newS;}); ⑤
return null; },
initTabsel : function() {
var th = this, c = this.items;

for (var i = 0;i < this.items.length; i++) ...{
var p = c.itemAt(i), t = p.items.itemAt(0);
p.tree = t;
t.selModel = new Ext.TabEvent( ...{tabBox : this,tabPanel : p});
}},
var tb = t.tabBox, tp = t.tabPanel; ①
var titems = tb.items, tlen = titems.length;
if (!tb.ativeTab) tb.ativeTab = tp; ②
var index = titems.indexOf(tb.ativeTab);
index = index < 0 ? 0 : index;
tb.ativeTab = titems.itemAt((index + tlen + 1) % tlen); ③
tb.ativeTab.expand(true);
var atree = tb.ativeTab.tree;
atree.getSelectionModel().select(atree.getRootNode().firstChild);
tp.collapse(true); ④
tb.doLayout();
if (Ext.isIE) Ext.override(Ext.tree.TreeNode,{preventHScroll:false});
tree.el.on("keydown", this.onKeyDown, this);
tb.ativeTab.on('expand', function() {
var atree = tb.ativeTab.tree;
atree.getSelectionModel().select(atree.getRootNode().firstChild);

tb.items.each(function() ...{
if (this != tb.ativeTab) ...{this.collapse(true); }
});}, this);
tb.ativeTab.expand(true);
select : function(node) {
this.msg = this.msg + " " + node.text;
this.tree = node.getOwnerTree();
this.tabPanel = this.tree.tabPanel;
return Ext.TabSel.superclass.select.call(this, node);
}
Ext.TabSel = function(config) {
Ext.TabSel.superclass.constructor.call(this);
Ext.apply(this, config);
this.init();};

Ext.extend(Ext.TabSel, Ext.tree.DefaultSelectionModel, ...{ msg : '',

init : function(tree) ...{
this.tlen = this.tabBox.items.length;

this.keynav = new Ext.KeyNav(Ext.getBody(), ...{ ①
'enter' : this.enter,'left' : this.left,
'right' : this.right, 'up' : this.up,
'down' : this.down, scope : this});

for (var i = 0;i < this.tabs.length; i++) ...{
this.tabs.itemAt(i).tree.on("click",this.onNodeClick,this);}

new Ext.KeyMap(Ext.getBody(), [...{ ②
key : 'n', alt : true,
fn : function() ...{ alert(this.msg); },
scope : this}]); },
.. .. ..});