当时写此程序是为了满足用电视(机顶盒)的遥控操作电视中的WEB页面(电视中的网页app)的需求
/**
* 按键导航(页面网格导航)
* FILE: keynav.js
* USEAGE:
* 每个参与导航的元素HTML写法如下:
* HTML: <a href="#" οnclick="location.href=this.href或JS方法调用" class="keynav" id="myId" keynav="idU,idR,idD,idL,1,1,move">item</a>
* 调用如下方法进行按键监听:
* JS: $('.keynav').keyNav(config); // 不指定config则使用默认值,通过AJAX取回数据并显示后,重新调用该方法使导航生效
* 属性说明:
* keynav的值为逗号分隔串:"idU,idR,idD,idL,canBack,isClick,funcMove"
* idU:上元素ID
* idR:右元素ID
* idD:下元素ID
* idL:左元素ID
* canBack:按后退键时1是0否可以后退到该元素
* isClick:元素获得焦点后是否立即触发其单击事件
* funcMove:函数名,当按上下左右键时使用此函数移动该元素
* @author tshichun 2010.10.26
*/
(function($) {
$.KeyNav = new Object();
/*main*/
$.fn.keyNav = function(config) {
var kn = $.KeyNav;
/*if (!kn.initialized)*/
if (kn.lastActive && kn.config) {
$(kn.lastActive).removeClass(kn.config.activeCls);
}
$(document).unbind('keydown');
kn.config = config ? $.extend({},
$.KeyNav.defaults, config) : $.KeyNav.defaults;
kn.navObjs = new Array();
/*所有导航对象*/
kn.active = null;
/*当前被激活的导航项*/
kn.lastActive = null;
/*上次被激活的导航项*/
if (typeof(kn.backActives) == 'undefined') {
kn.backActives = new Array();
}
/*按返回键则向上逐级激活这些元素*/
/*kn.initialized = true; endif*/
/*注册所有导航项*/
this.each(function() {
$.KeyNav.register(this);
});
/*设置默认激活的导航项*/
kn.active = $.KeyNav.getActive();
kn.lastActive = kn.active;
/*开始监听按键事件*/
$(document).keydown(function(e) {
var key = e == null ? event.keyCode: e.which;
switch (key) {
case 37:
$.KeyNav.doLeft(e);
break;
case 38:
$.KeyNav.doUp(e);
break;
case 39:
$.KeyNav.doRight(e);
break;
case 40:
$.KeyNav.doDown(e);
break;
case 13:
$.KeyNav.doEnter(e);
break;
case 8:
$.KeyNav.doBack(e);
break;
default:
break;
}
});
return this;
};
/*默认配置*/
$.KeyNav.defaults = {
activeCls: 'active',
/*被激活元素的class属性*/
activeDefault: 'keyNavDefault'
/*默认激活的元素ID*/
};
/*注册一个导航元素*/
$.KeyNav.register = function(element) {
var kn = $.KeyNav;
var obj = $(element);
var c = obj.attr('keynav');
if (c) {
c = c.split(',');
obj.U = c[0];
obj.R = c[1];
obj.D = c[2];
obj.L = c[3];
if (c[4] && c[4] == 1) {
obj.canBack = true;
}
if (c[5] && c[5] == 1) {
obj.isClick = true;
}
if (c[6] && c[6] != '') {
obj.funcMove = c[6];
}
}
kn.navObjs[obj.attr('id')] = obj;
};
/*激活一个导航项*/
$.KeyNav.setActive = function(obj) {
var kn = $.KeyNav;
if (obj != kn.active) {
kn.lastActive = kn.active;
$(kn.lastActive).trigger('blur');
$(kn.lastActive).removeClass(kn.config.activeCls);
$(obj).trigger('focus');
$(obj).addClass(kn.config.activeCls);
kn.active = obj;
/*元素可后退*/
if (obj.canBack) {
kn.uniquePush(kn.backActives, obj);
}
/*若要求立即触发单击事件,且不是默认激活的元素*/
if (obj.isClick && obj.attr('id') != $('#' + kn.config.activeDefault).attr('id')) {
$(obj).click();
}
}
};
/*当前被激活的导航项*/
$.KeyNav.getActive = function() {
var kn = $.KeyNav;
/*若当前没有被激活的导航项,则返回默认激活项*/
if (!kn.active && kn.navObjs[kn.config.activeDefault]) {
$.KeyNav.setActive(kn.navObjs[kn.config.activeDefault]);
}
return kn.active;
};
/*导航*/
$.KeyNav.nav = function(navObjsIndex) {
var kn = $.KeyNav;
var active = $.KeyNav.getActive();
if (active.funcMove) {
var e = typeof(arguments[1]) ? arguments[1] : null;
try {
eval(active.funcMove + '(e);');
} catch(err) {}
} else {
/*如果给定的元素存在*/
if (navObjsIndex && $('#' + navObjsIndex)[0] && kn.navObjs[navObjsIndex]) {
$.KeyNav.setActive(kn.navObjs[navObjsIndex]);
}
}
};
/*阻止浏览器默认事件行为*/
$.KeyNav.stopDefault = function(e) {
e == null ? window.event.returnValue = false: e.preventDefault();
};
/*向数组插入元素,若已存在,则忽略*/
$.KeyNav.uniquePush = function(array, obj) {
var exists = false;
for (var i = 0; i < array.length; i++) {
if ($(array[i]).attr('id') == $(obj).attr('id')) {
exists = true;
break;
}
}
if (!exists) {
array.push(obj);
}
};
/*按键处理*/
$.KeyNav.doLeft = function(e) {
var active = $.KeyNav.getActive();
$.KeyNav.nav(active.L, e);
};
$.KeyNav.doUp = function(e) {
var active = $.KeyNav.getActive();
$.KeyNav.nav(active.U, e);
};
$.KeyNav.doRight = function(e) {
var active = $.KeyNav.getActive();
$.KeyNav.nav(active.R, e);
};
$.KeyNav.doDown = function(e) {
var active = $.KeyNav.getActive();
$.KeyNav.nav(active.D, e);
};
$.KeyNav.doEnter = function(e) {
var active = $.KeyNav.getActive();
$(active).click();
};
$.KeyNav.doBack = function(e) {
$.KeyNav.stopDefault(e);
var kn = $.KeyNav;
var backHistory = true;
if (kn.backActives.length > 0) {
var index = kn.backActives.length - 1;
/*后进先出*/
/*只后退到非当前元素*/
if ($(kn.backActives[index]).attr('id') == $(kn.active).attr('id')) {
kn.backActives.splice(index, 1);
index--;
}
if (index >= 0) {
kn.setActive(kn.backActives[index]);
backHistory = false;
}
}
if (backHistory) {
history.back();
}
};
/*end*/
})(jQuery);
辅助函数(JS):
/**
* 按键导航(页面网格导航)工具函数
* FILE: keynav.tool.js
* 包括如下工具函数:
* 1.getKeynav 取得导航元素keynav属性值
* 2.getPageBar 取得分页条HTML
* USAGE:
* $.KeyNav.getKeynav(参数列表);
* $.KeyNav.getPageBar(参数列表);
* @author tshichun 2010.11.06
*/
(function($) {
/**
* 取得导航元素keynav属性值
* @param index 当前下标
* @param idPrefix ID前缀
* @param idUDefault 默认向上元素ID
* @param idRDefault 默认向右元素ID
* @param idDDefault 默认向下元素ID
* @param idLDefault 默认向左元素ID
* @param gridCols 网格列数
* @param gridRows 网格行数
* @param count 循环次数
* @author SYS 2010.11.06
*/
$.KeyNav.getKeynav = function(index, idPrefix, idUDefault, idRDefault, idDDefault, idLDefault, gridCols, gridRows, count) {
var idU = idR = idD = idL = '';
if (gridCols > 1 && gridRows > 1) {
// 行首元素的idL为idLDefault
idL = (index % gridCols == 0) ? idLDefault: idPrefix + (index - 1);
// 行尾元素的idR为idRDefault
idR = ((index + 1) % gridCols == 0) || ((index + 1) == count) ? idRDefault: idPrefix + (index + 1);
// 首行元素的idU为idUDefault
idU = (index < gridCols) ? idUDefault: idPrefix + (index - gridCols);
// 尾行元素的idD为idDDefault
idD = (Math.floor(index / gridCols) + 1) == Math.ceil(count / gridCols) || Math.ceil(count / gridCols) == 1 ? idDDefault: idPrefix + (index + gridCols);
} else if (gridRows == 1) {
// 只有一行的情况
idU = idUDefault;
idD = idDDefault;
idL = (index > 0) ? idPrefix + (index - 1) : idLDefault;
idR = (index < count - 1) ? idPrefix + (index + 1) : idRDefault;
} else if (gridCols == 1) {
// 只有一列的情况
idL = idLDefault;
idR = idRDefault;
idU = (index > 0) ? idPrefix + (index - 1) : idUDefault;
idD = (index < count - 1) ? idPrefix + (index + 1) : idDDefault;
}
return idU + ',' + idR + ',' + idD + ',' + idL;
};
/**
* 取得分页条HTML
* @param total 总记录数
* @param page 当前第几页
* @param pageSize 每页多少条记录
* @param jsMethod 通过JS方法取数据时有用,若指定该值,则忽略url
* @param pagesNum 显示多少页
* @param url 跳转链接地址
* @param array keyNavParams 需要通过键盘导航时指定该参数
* @param pageVar 页码对应的参数名,如?page=n中的page
* @author tshichun 2010.11.06
*/
$.KeyNav.getPageBar = function(total, page, pageSize, jsMethod, pagesNum, url, keyNavParams, pageVar) {
var pageBar = '';
if (total > pageSize) {
// 计算总页数
totalPages = Math.ceil(total / pageSize);
// 控制在最多pagesNum页
if (totalPages > pagesNum) {
totalPages = pagesNum;
}
if (jsMethod != '') {
// 通过JS方法调用
url = 'javascript:' + jsMethod + '(%p%)';
} else {
// 通过超链接形式
if (url == '') {
url = location.href;
}
url = url.replace(new RegExp('&*' + pageVar + '=' + page), '');
if (url.indexOf('?') != -1) {
url = url.indexOf('=') != -1 ? url + '&' + pageVar + '=%p%': url + pageVar + '=%p%';
} else {
url = url + '?' + pageVar + '=%p%';
}
}
// 需要通过键盘导航吗
var byKey = keyNavParams.length > 0;
var byKeyStr = '';
// 首页
if (byKey) {
byKeyStr = ' class="keynav" id="' + keyNavParams['prefix'] + '0" keynav="' + keyNavParams['idUDefault'] + ',' + keyNavParams['prefix'] + '1,' + keyNavParams['idDDefault'] + ',' + keyNavParams['idLDefault'] + '"';
} else {
byKeyStr = '';
}
pageBar += '<a href="' + (page == 1 ? 'javascript:;': url.replace('%p%', 1)) + '" οnclick="location.href=this.href"' + byKeyStr + '>首页</a>';
// 上一页
if (byKey) {
byKeyStr = ' class="keynav" id="' + keyNavParams['prefix'] + '1" keynav="' + keyNavParams['idUDefault'] + ',' + keyNavParams['prefix'] + '2,' + keyNavParams['idDDefault'] + ',' + keyNavParams['prefix'] + '0"';
} else {
byKeyStr = '';
}
pageBar += '<a href="' + (page > 1 ? url.replace('%p%', page - 1) : 'javascript:;') + '" οnclick="location.href=this.href"' + byKeyStr + '>上一页</a>';
// 下一页
if (byKey) {
byKeyStr = ' class="keynav" id="' + keyNavParams['prefix'] + '2" keynav="' + keyNavParams['idUDefault'] + ',' + keyNavParams['prefix'] + '3,' + keyNavParams['idDDefault'] + ',' + keyNavParams['prefix'] + '1"';
} else {
byKeyStr = '';
}
pageBar += '<a href="' + (page < totalPages ? url.replace('%p%', page + 1) : 'javascript:;') + '" οnclick="location.href=this.href"' + byKeyStr + '>下一页</a>';
// 尾页
if (byKey) {
byKeyStr = ' class="keynav" id="' + keyNavParams['prefix'] + '3" keynav="' + keyNavParams['idUDefault'] + ',' + keyNavParams['prefix'] + '4,' + keyNavParams['idDDefault'] + ',' + keyNavParams['prefix'] + '2"';
} else {
byKeyStr = '';
}
pageBar += '<a href="' + (page == totalPages ? 'javascript:;': url.replace('%p%', totalPages)) + '" οnclick="location.href=this.href"' + byKeyStr + '>尾页</a>';
// 显示下拉页码
if (byKey) {
byKeyStr = ' class="keynav" id="' + keyNavParams['prefix'] + '4" keynav=",' + keyNavParams['idRDefault'] + ',' + keyNavParams['idDDefault'] + ',' + keyNavParams['prefix'] + '3"';
} else {
byKeyStr = '';
}
//下拉列表单击事件处理
var onclick = '';
if (jsMethod) {
onclick = url.replace('%p%)', '') + "this.value)";
} else {
onclick = "location.href='" + url.replace('%p%', '') + "' + this.value";
}
pageBar += ' 跳转至 <select' + byKeyStr + ' οnclick="' + onclick + '">';
for (var i = 0; i < totalPages; i++) {
var selected = (i + 1) == page ? ' selected': '';
pageBar += '<option value="' + (i + 1) + '"' + selected + '>' + (i + 1) + '</option>';
}
pageBar += '</select> 页';
}
return pageBar;
};
})(jQuery);
辅助函数(PHP):
/**
* 取得由导航元素的上右下左邻元素ID组成的字符串
* 如:idU,idR,idD,idL
* @param $params array
* (
* index 当前下标
* idPrefix ID前缀
* idUDefault 默认向上元素ID
* idRDefault 默认向右元素ID
* idDDefault 默认向下元素ID
* idLDefault 默认向左元素ID
* gridCols 网格列数
* gridRows 网格行数
* count 循环次数
* )
* @author tshichun 2010.10.26
*/
function getKNStrForEach(array $params) {
extract($params);
$idU = $idR = $idD = $idL = '';
if ($gridCols > 1 && $gridRows > 1) {
// 行首元素的$idL为$idLDefault
$idL = ($index % $gridCols == 0) ? $idLDefault: $idPrefix. ($index - 1);
// 行尾元素的$idR为$idRDefault
$idR = (($index + 1) % $gridCols == 0) || (($index + 1) == $count) ? $idRDefault: $idPrefix. ($index + 1);
// 首行元素的$idU为$idUDefault
$idU = ($index < $gridCols) ? $idUDefault: $idPrefix. ($index - $gridCols);
// 尾行元素的$idD为$idDDefault
$idD = (floor($index / $gridCols) + 1) == ceil($count / $gridCols) || ceil($count / $gridCols) == 1 ? $idDDefault: $idPrefix. ($index + $gridCols);
} else if ($gridRows == 1) {
// 只有一行的情况
$idU = $idUDefault;
$idD = $idDDefault;
$idL = ($index > 0) ? $idPrefix. ($index - 1) : $idLDefault;
$idR = ($index < $count - 1) ? $idPrefix. ($index + 1) : $idRDefault;
} else if ($gridCols == 1) {
// 只有一列的情况
$idL = $idLDefault;
$idR = $idRDefault;
$idU = ($index > 0) ? $idPrefix. ($index - 1) : $idUDefault;
$idD = ($index < $count - 1) ? $idPrefix. ($index + 1) : $idDDefault;
}
return "$idU,$idR,$idD,$idL";
}
/**
* 取得分页导航条
* @param $total 总记录数
* @param $page 当前第几页
* @param $pageSize 每页多少条记录
* @param $jsMethod 通过JS方法取数据时有用,若指定该值,则忽略$url
* @param $pagesNum 显示多少页
* @param $url 跳转链接地址
* @param array $keyNavParams 需要通过键盘导航时指定该参数
* @param $pageVar 页码对应的参数名,如?page=n中的page
* @author tshichun 2010.10.27
*/
function getPageBar($total, $page, $pageSize, $jsMethod = '', $pagesNum = 20, $url = '', array $keyNavParams = null, $pageVar = 'page') {
$pageBar = '';
if ($total > $pageSize) {
// 计算总页数
$totalPages = ceil($total / $pageSize);
// 控制在最多$pagesNum页
if ($totalPages > $pagesNum) {
$totalPages = $pagesNum;
}
if ($jsMethod != '') {
// 通过JS方法调用
$url = 'javascript:'.$jsMethod.'(%p%)';
} else {
// 通过超链接形式
if ($url == '') {
$url = $_SERVER['REQUEST_URI'];
}
$url = ereg_replace("(^|&)".$pageVar."=$page", '', $url);
if (strpos($url, '?')) {
$url = strpos($url, '=') ? $url.'&'.$pageVar.'=%p%': $url.$pageVar.'=%p%';
} else {
$url = $url.'?'.$pageVar.'=%p%';
}
}
// 需要通过键盘导航吗
$byKey = !empty($keyNavParams);
// 首页
if ($byKey) {
$byKeyStr = ' class="keynav" id="'.$keyNavParams['prefix'].'0" keynav="'.$keyNavParams['idUDefault'].','.$keyNavParams['prefix'].'1,'.$keyNavParams['idDDefault'].','.$keyNavParams['idLDefault'].'"';
} else {
$byKeyStr = '';
}
$pageBar. = '<a href="'. ($page == 1 ? 'javascript:;': str_replace('%p%', 1, $url)).'" οnclick="location.href=this.href"'.$byKeyStr.'>首页</a>';
// 上一页
if ($byKey) {
$byKeyStr = ' class="keynav" id="'.$keyNavParams['prefix'].'1" keynav="'.$keyNavParams['idUDefault'].','.$keyNavParams['prefix'].'2,'.$keyNavParams['idDDefault'].','.$keyNavParams['prefix'].'0"';
} else {
$byKeyStr = '';
}
$pageBar. = '<a href="'. ($page > 1 ? str_replace('%p%', $page - 1, $url) : 'javascript:;').'" οnclick="location.href=this.href"'.$byKeyStr.'>上一页</a>';
// 下一页
if ($byKey) {
$byKeyStr = ' class="keynav" id="'.$keyNavParams['prefix'].'2" keynav="'.$keyNavParams['idUDefault'].','.$keyNavParams['prefix'].'3,'.$keyNavParams['idDDefault'].','.$keyNavParams['prefix'].'1"';
} else {
$byKeyStr = '';
}
$pageBar. = '<a href="'. ($page < $totalPages ? str_replace('%p%', $page + 1, $url) : 'javascript:;').'" οnclick="location.href=this.href"'.$byKeyStr.'>下一页</a>';
// 尾页
if ($byKey) {
$byKeyStr = ' class="keynav" id="'.$keyNavParams['prefix'].'3" keynav="'.$keyNavParams['idUDefault'].','.$keyNavParams['prefix'].'4,'.$keyNavParams['idDDefault'].','.$keyNavParams['prefix'].'2"';
} else {
$byKeyStr = '';
}
$pageBar. = '<a href="'. ($page == $totalPages ? 'javascript:;': str_replace('%p%', $totalPages, $url)).'" οnclick="location.href=this.href"'.$byKeyStr.'>尾页</a>';
// 显示下拉页码
if ($byKey) {
$byKeyStr = ' class="keynav" id="'.$keyNavParams['prefix'].'4" keynav=",'.$keyNavParams['idRDefault'].','.$keyNavParams['idDDefault'].','.$keyNavParams['prefix'].'3"';
} else {
$byKeyStr = '';
}
//下拉列表单击事件处理
if ($jsMethod) {
$onclick = str_replace('%p%)', '', $url)."this.value)";
} else {
$onclick = "location.href='".str_replace('%p%', '', $url)."' + this.value";
}
$pageBar. = ' 跳转至 <select'.$byKeyStr.' οnclick="'.$onclick.'">';
for ($i = 0; $i < $totalPages; $i++) {
$selected = ($i + 1) == $page ? ' selected': '';
$pageBar. = '<option value="'. ($i + 1).'"'.$selected.'>'. ($i + 1).'</option>';
}
$pageBar. = '</select> 页';
}
return $pageBar;
}