使用easyui中的tab组件,每个tab页面都是一个内嵌iframe,当在界面上鼠标几点做tab页切换的时候,每次内部页面的滚动条都会自动到顶端,这样给使用上带来了很大的不方便。接下来,我们看如何来优化这个功能。

easyui中tab组件每次切换tab页时内部页面滚动条到顶端问题修改_全局变量

首先,我们想到的时候给tab添加两个事件:onSelect和onUnSelect,当离开的时候(unSelect)记录下当前页面的滚动条高度,在进入的时候(select)使用js设置页面的滚动条高度。我们来分解,一步一步的实现。

1、需要一个map数据结构,记录每个tab页的滚动条高度:

function Map() {
var struct = function(key, value) {
this.key = key;
this.value = value
};
var put = function(key, value) {
for (var i = 0; i < this.arr.length; i++) {
if (this.arr[i].key === key) {
this.arr[i].value = value;
return
}
}
this.arr[this.arr.length] = new struct(key, value)
};
var get = function(key) {
for (var i = 0; i < this.arr.length; i++) {
if (this.arr[i].key === key) {
return this.arr[i].value
}
}
return null
};
var remove = function(key) {
var v;
for (var i = 0; i < this.arr.length; i++) {
v = this.arr.pop();
if (v.key === key) {
continue
}
this.arr.unshift(v)
}
};
var getAllValues = function() {
var tmp = new Array();
for (var i = 0; i < this.arr.length; i++) {
tmp[i] = this.arr[i].value
}
return tmp
};
var getAllKeys = function() {
var tmp = new Array();
for (var i = 0; i < this.arr.length; i++) {
tmp[i] = this.arr[i].key
}
return tmp
};
var size = function() {
return this.arr.length
};
var isEmpty = function() {
return this.arr.length <= 0
};
var removeAll = function() {
this.arr = []
};
this.getFirstValue = function() {
return this.arr[0].value
};
this.getFirstKey = function() {
return this.arr[0].key
};
this.arr = new Array();
this.get = get;
this.put = put;
this.remove = remove;
this.size = size;
this.isEmpty = isEmpty;
this.removeAll = removeAll;
this.getAllValues = getAllValues;
this.getAllKeys = getAllKeys;
};

2、在主页面(index)上,给tab组件添加事件

//定义全局变量
var scrollMap = new Map();
var selectedIndex = 0;
subPageScrollHeight = 0;
$(function(){
$("#tabs_index").tabs({
onSelect:function(title,index){
//获取index
//var tab = $('#tabs_index').tabs('getSelected');
//selectedIndex = $('#tabs_index').tabs('getTabIndex',tab);
selectedIndex = index;

//设置位置
var val = scrollMap.get(selectedIndex);
if (!isNull(val)) {
$("#tabs_index > div").eq(1).children("div").eq(selectedIndex).find("iframe")[0].contentWindow.scrollTo(0,val);
}
}
});
$("#tabs_index").tabs({
onUnselect:function(title,index){
if (selectedIndex>0) {
//这种获取子页面滚动条高度失败,始终是0;改成了子页面监控自己的滚动条
//var h = $("#tabs_index > div").eq(1).children("div").eq(selectedIndex).find("iframe")[0].contentWindow.document.documentElement.scrollTop;

//这种格式获取子页面变量值 失败!改成子页面修改父页面值
//var ifname = $("#tabs_index > div").eq(1).children("div").eq(selectedIndex).find("iframe")[0].name;
//var h1 = document.frames[ifname].hhh;

//alert(title+"is unselected,"+selectedIndex+",h:"+subPageScrollHeight);
scrollMap.put(index,subPageScrollHeight);
}
}
});
});

scrollMap:用来存储tab页面和高度的,key是tabIndex;

selectedIndex:当前激活的tabIndex;

subPageScrollHeight:父页面(index)的全局变量,供子页面(iframe)去修改,记录子页面滚动条高度。

这里在处理离开(onSelect)逻辑时走了一些弯路,具体分析一下:

1)在离开时获取内部(iframe)页面的滚动条,然后存到map中:

var h = $("#tabs_index > div").eq(1).children("div").eq(selectedIndex).find("iframe")[0].contentWindow.document.documentElement.scrollTop;

通过观察html可以发现,easyui会给tab组件生成两个div,第一个是tab的标题,第二个是tab的内容,在第二个div中每个页面又是一个div,所以我们可以通过上面jquery的筛选来获得到对应iframe的高度。

但是,每次这个h都是0。为什么呢?开始怀疑方法错了,但是后来想明白了,这不就是问题的本身吗:easyui每次在离开的时候会把内部页面的滚动条置顶,所以每次h都是0 了。

2)每个子页面(iframe)都保存一个全局变量h,各自页面来维护其值:

上面方式行不通,换一个思路,让每个子页面(iframe)都保存一个全局变量h,各自页面来维护其值,父页面(index)中,tab离开(unSelect)的时候获取子页面的值,保存到map中。这就需要定义一个公共的onScroll监听事件,来监听每个子页面的滚动了。

$(window).scroll(function(){
hhh = $(document).scrollTop();
});

然后再Unselect中,使用如下方法获取:

//var ifname = $("#tabs_index > div").eq(1).children("div").eq(selectedIndex).find("iframe")[0].name;
//var h1 = document.frames[ifname].hhh;

经过实验使用获取不到值,原因​

所以,只要再更换思路。

3)主页面(index)中定义全局变量subPageScrollHeight,让各个子页面去更新这个值:

所以,公共的监控函数修改成:

$(window).scroll(function(){
//父页面值
parent.subPageScrollHeight = $(document).scrollTop();
});

这样,在父页面中,当tab离开时就可以获取到每个页面的高度了。

3、优化

讲过了上面的操作,已经可以实现了功能。但是经过测试,发现多切换几次,滚动条又会回到顶部。这是什么原因呢?

$(window).scroll(function(){
parent.subPageScrollHeight = $(document).scrollTop();
console.log(parent.subPageScrollHeight);
});

在scroll监听函数里面打印log,观察后发现:当切换tab时,每次打印行日志,第一行是一个正常的高度(例如3009),第二行每次都是0。这又是为什么呢?

继续思考,又回到了开始的问题:easyui每次在离开(unselect)的时候会把内部页面的滚动条置顶。所以整个过程是:

1)进入(select)的时候,我们会手动把下个页面滚动高度设置成正常值,所以会触发一次scroll事件,故第一次打印的是正常值;

2)但是在离开的时候tab每次又把上一个页面置顶,又出发了一次scroll事件,在主页面的全局变量(subPageScrollHeight )就被设置成了0 。这样,切换两次后,就会出现不对的情况(又置顶了)

那如何解决呢?

在统一监听代码中做如下改造:

$(window).scroll(function(){
//父页面值
if ($(document).scrollTop() > 0) {
parent.subPageScrollHeight = $(document).scrollTop();
}
});

这样,就避免了“离开的时候tab每次又把上一个页面置顶,又出发了一次scroll事件,在主页面的全局变量就被设置成了0”这个问题。但又有人会问,那我切换完tab后,主动将页面放到最上端,岂不是会有问题了?

答案是不会的,因为scroll会一直监听,及时手动将页面放到了最上部,subPageScrollHeight 全局变量会是一个很接近的0的数,比如1,但就是不会是0.这样,0和1之间的差别不会影响前端体验,都是在最上端。

后记:

经过上面优化,tab已经没有任何问题了。但是,还是会有一定的优化空间,例如:统一监听scroll时间会一直执行,能否“稀释”这个函数,但又要保证正确性呢?

当然后,答案就是​