主题:  真正的下拉菜单

风之翅

职务:普通成员
等级:2
金币:10.0
发贴:540
注册:2001/1/31 14:48:59
#12001/11/1 13:14:50
一般的下拉菜单用的都是绝对坐标,当分辨率或窗口大小变化都会变形。一直想找一个随页面自动变化的下拉菜单。前几天在QYworld兄的主页看到了这样的效果,另存下来研究了一下,找到了方法。原理就是用javascript程序动态定位子菜单的坐标,再写入文档。先看看效果:

页面:点这儿参观
里面有一个外部链接的JS文件,那是真正的重头戏,代码我都做了注释,可能有些难,有个地方我也有点含糊,还要请教高手。
如果你没有兴趣研究,也可以直接引用。打开窗口后,另存后就可以找到这个名字为bar.js的文件。
页面本身的HTML代码应该能看得懂。有颜色的代码都是比较重要的,红色部分可以修改,蓝色部分是关键代码,不是太懂,最好不要修改。
页面源代码如下:


下拉菜单






onmousemove=HideMenu(); onload="" text=#000000 bgColor=#FFFFFF>









onmouseover="ShowMenu('[color=red]子菜单一
子菜单二
子菜单三
')" [/color]
href="#">主菜单一
onmouseover="ShowMenu('[color=red]子菜单一
子菜单二
子菜单三
')" [/color]
href="#">主菜单二
<onmouseover="ShowMenu('[color=red]子菜单一
子菜单二
子菜单三
')" [/color]
href="#">主菜单三





风之翅

职务:普通成员
等级:2
金币:10.0
发贴:540
注册:2001/1/31 14:48:59
#22001/11/1 13:30:21
请教高手
JS文件的源代码部分有些不明白,请指教。。。
先看源代码:
    if (isvisible == true)
{
        vDiv = document.all("menuDiv");
        mX = window.event.clientX + document.body.scrollLeft;//检索到鼠标移动到的X坐标+横向滚动条位置
        mY = window.event.clientY + document.body.scrollTop;//检索到鼠标移动到的Y坐标+纵向滚动条位置
        if ((mX < parseInt(vDiv.style.left)) || (mX > parseInt(vDiv.style.left)+vDiv.offsetWidth) || (mY < parseInt(vDiv.style.top)-h) || (mY > parseInt(vDiv.style.top)+vDiv.offsetHeight)){//简单的说法就是比较鼠标坐标值与子菜单坐标值
            vDiv.style.visibility = "hidden";//只要满足以上任一条件,子菜单就隐藏
            isvisible = false;//返回一个布鲁值后结束
        }
    }
}

function ShowMenu(vMnuCode) {
    vSrc = window.event.srcElement;//检索事件的对象,在这里就是检索到鼠标移到对象上而找到主菜单元素
    vMnuCode = "
" + vMnuCode + "
";//得到子菜单的HTML文本,在这里可以定义子菜单的外观
    h = vSrc.offsetHeight + 4;//主菜单本身高度+主菜单元素本身高度+4,可以通过调整此数字控制主菜单和子菜单的间距,此值很重要,因为在HideMenu()中要调用,如果不要此值,就不能在t后面添加,否则会使菜单显示不正常
    l = vSrc.offsetLeft - 10;//主菜单的X坐标-10,可以通过调整此数字控制主菜单和子菜单的缩进
    t = vSrc.offsetTop + h;//主菜单的Y坐标
    vParent = vSrc.offsetParent;//定义元素的坐标系统
    while (vParent.tagName.toUpperCase() != "BODY")//在vParent元素的标记符是大写BODY之前循环,因为在后面设置innerHTML属性时,不能在文档加载时设置
    {
        l += vParent.offsetLeft;//+=左运算数加右运算数,和赋值给左运算数
        t += vParent.offsetTop;
        vParent = vParent.offsetParent;//重新定义元素的坐标系统
    }


    menuDiv.innerHTML = vMnuCode;//interHTML:元素包含的HTML文本。可以用指定的HTML文本替换元素的内容,在这里就是利用它把子菜单的HTML文本加入menuDIV对象的标记符内。
    menuDiv.style.top = t;//设置top值
    menuDiv.style.left = l;//设置left值
    menuDiv.style.visibility = "visible";//设置显示
    isvisible = true;//返回一个布鲁值,以便HideMenu()调用
    
}
//-->
注释是我自己的理解,如果有错误的地方请指出。。。
现在有这样几个问题要问:
1.红色部分代码的含义
2.红色部分代码是否与页面代码中的onload=""有联系,因为我在页面代码中去掉了onload="",没有任何影响。



helmet

职务:版主
等级:4
金币:10.0
发贴:1559
注册:2004/1/30 15:59:27
#32001/11/4 2:13:26
你分析的对:
(红字部分)
1,vParent.offsetParent是相对的坐标系统,是相对于鼠标触发事件的元素标签(tagName),
由于vParent的最终是要相对于body的,而当时,鼠标触发的是
,所以,第一次循环
以后,vParent.tagName就变成了也就是表格tag,第二次循环后,vParent.tagName
才为body。
这样,menuDiv的位置,也就是menuDiv.style.top才可以被t符值。因为此时的t
才是真正的相对于浏览器文档窗口。

2,while (vParent.tagName.toUpperCase() != "BODY"){}的意思是:
忽略大小写,只要是body标签,即停止循环,因为这时已经是相对于body的坐标系统了。



风之翅

职务:普通成员
等级:2
金币:10.0
发贴:540
注册:2001/1/31 14:48:59
#42001/11/4 11:37:56
谢谢deathcult了,这下终于懂了。
但那个关于body大写的问题,好像应该是在js程序中,body标签必须大写。我是在经典论坛里看到的,比如tr,td之类都必须大写。
再更正我的几个错误
1.在页面文件中的CSS样式表写错了,text-decoration: no,应该是none,大意了,所以a { font-family: "Verdana", "Arial", "新宋体", "宋体"; font-size: 9pt; color: #000000; text-decoration: none}就没有意义了,
2.isvisible = false;//返回一个布鲁值后结束,应该是布尔值,好菜啊!
deathcult兄好给我留面子啊,再次谢谢

编辑历史:[这消息被jhsdx编辑过(编辑时间2001-11-04 11:46:44)]


helmet

职务:版主
等级:4
金币:10.0
发贴:1559
注册:2004/1/30 15:59:27
#52001/11/4 14:41:56
谢什么啊,不客气,咱们使劲讨论! ;)

关于body大写的问题:
我还是认为我的观点对:即“忽略大小写,只要是body标签,即停止循环,因为这时已经是相对于body的坐标系统了。”

你可以把程序这样改,看看菜单的效果改没改变:
1,//while (vParent.tagName.toUpperCase() != "BODY")
while (vParent.tagName.toLowerCase() != "body")

2,如果你的html文件中,是BODY,也可以这样写while (vParent.tagName != "BODY") 。


我想是由于vParent.tagName没法判定用户的body是怎么写的,比如Body,boDy,bOdy等等,
所以就将其一律转化成大写(或小写),好在循环的条件里比较啊。


每页显示15条 1/1 << <1 >>