使用的设计模式加方法:惰性单例+迭代
1、要求
- 在网页中鼠标右击后出现菜单,菜单中出现可选择的项,若有子菜单也需显示,就像浏览器自带的方法一样。
2、思路
- 因为部分条目拥有子菜单
故首先需要设计数据对象的格式。 - 鼠标覆盖在菜单中的拥有 子菜单的 ‘li’ 上时需要弹出子菜单,所以有两种解决思路
2.1 动态创建子菜单对象(首先使用的这种方法,后来因为粗心导致失败后放弃,后来在使用下面方法成功后察觉到问题所在)。
2.2 在加载右键菜单时,将所有的菜单都加载出来,鼠标不在的隐藏(考虑到菜单的内容并不多,最后采用这种方法)。 - 鼠标覆盖判断
3.1 鼠标覆盖时,若该条目存在子菜单则显示子菜单,鼠标离开该条目或子菜单时关闭子菜单(放弃该方法,也是这个思路导致了前面动态创建子菜单时出现bug)
3.2 鼠标覆盖该条目时,遍历其它条目,关闭其它同级项目的子菜单,然后显示该条目的菜单(采用这种方法,避免了鼠标移开一级菜单到二级菜单时,二级菜单被关闭的情况)
3、实现
- 部分样式代码
<style>
.list-box {
position: absolute;
border: 1px solid red;
}
.list-div {
position: absolute;
width: 150px;
border: 1px solid #333;
}
.list-ul{
position: absolute;
list-style: none;
}
li {
width: 150px;
border: 1px solid yellowgreen;
}
</style>
- 确定的数据格式
var listData = [
{
name: '百度',
url: 'https://www.baidu.com/',
child: [
{ name: '百度一下', url: 'https://www.baidu.com/' },
{ name: '百度翻译', url: 'https://fanyi.baidu.com/' }
]
},
{
name: '腾讯',
url: 'https://www.qq.com/',
child: [
{ name: '腾讯QQ', url: 'https://im.qq.com/', child: [{ name: 'QQ注册', url: 'https://ssl.zc.qq.com/v3/index-chs.html' }] },
{ name: '腾讯视频', url: 'https://v.qq.com/' }
]
},
{
name: '淘宝网',
url: 'https://www.taobao.com/'
}
];
- js 相关代码
3.1 屏蔽默认代码
// jQuery扩展代码
$.createEle = function (params) {
var tagName = params.tagName || 'div';
var id = params.id || '';
var className = params.className || '';
var content = params.content || '';
var css = params.css || {};
var dom = document.createElement(tagName);
if (id != '') { dom.id = id };
if (className != '') { dom.className = className };
if (content != '') { dom.innerHTML = content };
for (var o in css) {
dom.style[o] = css[o];
}
return dom;
};
document.oncontextmenu = function (ev) {
// 屏蔽代码
ev.preventDefault();
var x = ev.clientX;
var y = ev.clientY;
// SelectBox 右键框单例
var s = SelectBox.show({ left: x + 'px', top: y + 'px' });
};
3.2 单例代码
var SelectBox = (function () {
var listData = [
{
name: '百度',
url: 'https://www.baidu.com/',
child: [
{ name: '百度一下', url: 'https://www.baidu.com/' },
{ name: '百度翻译', url: 'https://fanyi.baidu.com/' }
]
},
{
name: '腾讯',
url: 'https://www.qq.com/',
child: [
{ name: '腾讯QQ', url: 'https://im.qq.com/', child: [{ name: 'QQ注册', url: 'https://ssl.zc.qq.com/v3/index-chs.html' }] },
{ name: '腾讯视频', url: 'https://v.qq.com/' }
]
},
{
name: '淘宝网',
url: 'https://www.taobao.com/'
}
];
var _containBox = null;
/**遍历数据
obj:数据
step:递归次数 控制展示位置
topRow:距离顶部多少格 控制展示位置
*/
function bianli(obj,step,topRow) {
var ul = $.createEle({ tagName: 'ul', className: 'list-ul' });
_containBox.appendChild(ul);
ul.style.position='absolute';
ul.style.left=150*step+'px';
ul.style.top=20*topRow+'px';
for (let i = 0; i < obj.length; i++) {
var liHTML = '<a href="' + obj[i].url + '">' + obj[i].name;
if (obj[i].child != null) {
liHTML += ' ></a>';
} else {
liHTML += '</a>';
};
var li = $.createEle({ tagName: 'li', className: 'list-ul-li', content: liHTML });
if (obj[i].child != null) {
li.child = bianli(obj[i].child,(step+1),(i+topRow));
li.child.style.display = 'none';
_containBox.appendChild(li.child);
};
li.onmouseover = function () {
for (var j = 0; j < ul.childNodes.length; j++) {
if (ul.childNodes[j] != li) {
ul.childNodes[j].closeChild();
}
}
if (this.child != null) {
this.child.style.display = 'block';
}
};
li.closeChild = function () {
if (this.child != null) {
this.child.style.display = 'none';
var childLis = this.child.childNodes;
for (var j = 0; j < childLis.length; j++) {
if (childLis[j].child != null) {
childLis[j].child.style.display = 'none';
for (var k = 0; k < childLis[j].child.childNodes.length; k++) {
childLis[j].child.childNodes[k].closeChild();
}
}
}
}
}
ul.appendChild(li);
}
return ul;
};
function getBox() {
if (!_containBox) {
_containBox = $.createEle({ tagName: 'div', className: 'list-box' });
bianli(listData,0,0);
document.body.appendChild(_containBox);
_containBox.style.display = 'none';
}
return _containBox;
};
return {
show: function (params) {
var Box = getBox();
Box.style.left = params.left;
Box.style.top = params.top;
Box.style.display = 'block';
},
close:function(){
var Box = getBox();
Box.style.display = 'none';
for(var i=0;i<_containBox.childNodes.length;i++){
if(i!=0){
_containBox.childNodes[i].style.display='none';
}
}
}
}
})();
- 效果图
- 总结
5.1 样式
样式方面就根据具体需求再美化了,部分距离设置方面可以配合 css 样式继续优化
5.2 层数
由于使用的迭代方法,所以只要数据中 child 不为 null 就可以无限嵌套,且无需手动调整。
5.3 若是有需要的话也可以设计成 动态生成子菜单的形式,一切都看需要吧。还有在写代码的过程中一定要细心。起初一句代码少写了 ‘.display’,导致我检查了很久。都对自己的代码产生了怀疑。。。。