您现在的位置是:首页 > cms教程 > Discuz教程Discuz教程

Discuz!NT控件左侧导航控件分析

紫山2025-07-02Discuz教程已有人查阅

导读其实这个控件的核心基本都在JS上,而相关的数据绑定和显示却非常简单。而需要说明的是在Discuz!NT的1.0和2.0正式版,这个控件做过一些调整,当然改动也基本上是在JS上

其实这个控件的核心基本都在JS上,而相关的数据绑定和显示却非常简单。而需要说明的是在Discuz!NT的1.0和2.0正式版,这个控件做过一些调整,当然改动也基本上是在JS上,今天给大家的源码是1.0正式版的代码,虽然有些“旧”,但程序本身的思想没变,大家只要明白了这里的源码,有了这碗酒垫底,相信再看即将开源的2.0代码,就会一目了然了。
好了,废话到此,马上开始今天的话题!首先将相应的C#代码放出来:
1#region Property ScriptPath
2
3/// <summary>
4/// Javascript脚本文件所在目录。
5/// </summary>
6[Description("Javascript脚本文件所在目录。"),DefaultValue("./")]
7public string ScriptPath
8{
9 get
10 {
11object obj = ViewState["NavMenuScriptPath"];
12return obj == null ? "js/Navbar.js" :(string) obj;
13 }
14 set
15 {
16ViewState["NavMenuScriptPath"] = value;
17 }
18}
19
20#endregion
21
22
23#region Property ImageUrl
24[Bindable(true), Category("Appearance"), DefaultValue("")]
25public string ImageUrl
26{
27 get
28 {
29if (base.ViewState["NavMenuimageurl"] != null)
30{
31 return (String)base.ViewState["NavMenuimageurl"];
32}
33else
34{
35 return "images/";//String.Empty;
36}
37 }
38 set
39 {
40base.ViewState["NavMenuimageurl"] = value;
41 }
42}
43
44#endregion
45
46
47#region Property CssPath
48
49/// <summary>
50/// Css文件所在目录。
51/// </summary>
52[Description("Javascript脚本文件所在目录。"),DefaultValue("./")]
53public string CssPath
54{
55 get
56 {
57object obj = ViewState["NavMenuCssPath"];
58return obj == null ? "styles/nav.css" :(string) obj;
59 }
60 set
61 {
62ViewState["NavMenuCssPath"] = value;
63 }
64}
65
66#endregion
67
68
69#region Property XmlFileFullPathName
70
71/// <summary>
72/// Xml文件所在目录。
73/// </summary>
74[Description("Xml文件所在目录。"),DefaultValue("./")]
75public string XmlFileFullPathName
76{
77 get
78 {
79object obj = ViewState["NavMenuXmlFileFullPathName"];
80return obj == null ? "xml/navmenu.xml" :(string) obj;
81 }
82 set
83 {
84ViewState["NavMenuXmlFileFullPathName"] = value;
85 }
86}
87
88#endregion
89
90#region protected override void OnPreRender(EventArgs e)
91/// <summary>
92/// 重写<see cref="System.Web.UI.Control.OnPreRender"/>方法。
93/// </summary>
94/// <param name="e">包含事件数据的 <see cref="EventArgs"/> 对象。</param>
95protected override void OnPreRender(EventArgs e)
96{
97 StringBuilder sb = new StringBuilder();
98
99 sb.Append("<script type=\"text/javascript\" src=\"" + this.ScriptPath + "\"></script>\r\n");
100 sb.Append("<link rel=\"stylesheet\" type=\"text/css\" href=\"" + this.CssPath + "\" />\r\n");
101 sb.Append("<script type=\"text/javascript\">var imgpath='" + this.ImageUrl + "';</script>\r\n");
102 sb.Append("<script language=\"javascript\" src=\"" + this.ScriptPath + "\"></script>\r\n");
103#if NET1
104if (!Page.IsClientScriptBlockRegistered("NavMenu"))
105{
106Page.RegisterClientScriptBlock("NavMenu", sb.ToString());
107}
108#else
109if (!Page.ClientScript.IsClientScriptBlockRegistered("NavMenu"))
110{
111Page.ClientScript.RegisterClientScriptBlock(this.GetType(), "NavMenu",sb.ToString());
112}
113#endif
114
115base.OnPreRender(e);
116}
117
118#endregion
119
120 /// <summary>
121 /// 将此控件呈现给指定的输出参数。
122 /// </summary>
123 /// <param name="output"> 要写出到的 HTML 编写器 </param>
124 protected override void Render(HtmlTextWriter output)
125 {
126
127output.Write("<div class=\"sdmenu\">\r\n");
128
129System.Data.DataSet dsSrc = new System.Data.DataSet();
130dsSrc.ReadXml(Page.Server.MapPath(this.XmlFileFullPathName));
131
132int count=0;
133foreach(System.Data.DataRow dr in dsSrc.Tables[0].Rows)
134{
135 #region 输出主菜单
136 output.Write(" <table>\r\n");
137 output.Write("<tr>\r\n");
138 if(count==0)
139 {
140output.Write("<td width=\"196px\"><span class=\"title\" id=\"top\">"+
141dr["menutitle"]+"</span></td>\r\n");
142 }
143 else
144 {
145output.Write("<td width=\"196px\"><span class=\"title\">"+
146dr["menutitle"]+"</span></td>\r\n");
147 }
148 output.Write("<td><img src=\""+this.ImageUrl+"/top_level_ico1.gif\"
149class=\"arrow\"/></td>\r\n");
150 output.Write("</tr>\r\n");
151 output.Write("</table>\r\n");
152 #endregion
153
154 #region 输出子菜单
155 output.Write(" <div class=\"submenu\">\r\n");
156 output.Write("<table>\r\n");
157 foreach(System.Data.DataRow drs in dsSrc.Tables[1].Select("menuparentid='"+
158dr["menuid"]+"'"))
159 {
160
161output.Write("<tr>\r\n");
162if(drs["imgurl"].ToString().Trim()!="")
163{
164 output.Write(" <td class=\"lefttd\" ><img src=\""+
165drs["imgurl"].ToString().Trim()+"\"
166class=\"submenuimg\" align=\"absmiddle\"/></td>");
167}
168else
169{
170 output.Write(" <td class=\"lefttd\" ></td>");
171}
172if((drs["frameid"].ToString().Trim()=="top")||(drs["frameid"].ToString().
173Trim()==""))
174{
175 output.Write("<td><a href=\"javascript:void(0);\"
176onclick=\"javascript:top.location.href='"+
177drs["link"].ToString().Trim()+"';\"
178onfocus=\"this.blur();\">"+
179drs["menutitle"].ToString().Trim()+"</a></td>\r\n");
180}
181else
182{
183 output.Write("<td><a href=\""+drs["link"].ToString().Trim()+"\"
184target="+drs["frameid"].ToString().Trim()+"
185onfocus=\"this.blur();\">"+
186drs["menutitle"].ToString().Trim()+"</a></td>\r\n");
187}
188output.Write("</tr>\r\n");
189 }
190 output.Write("</table>\r\n");
191 output.Write("</div>\r\n");
192 #endregion
193
194 //打印分割符
195 output.Write("<div class=\"splitter\"> </div>\r\n");
196 count++;
197 }
198
199 output.Write("</div>\r\n");
200}
201 }
上面的代码因为太简单,就不多做介绍了。而XML的结构如下:
其中的submain表(这里暂且这样说)的menuparentid(子菜单的父menuid),是关联mainmenu表的menuid,这样就能够这这两个表有一个主从结构了。当前如果将这两个表合成“一个”也可以,前提是要减少数据冗余,因为mainmenu表里是不包含link(点击子菜单跳转地址),frameid(子菜单跳转的frameid)这样的信息的。
之后要说明的是这个控件的JS,代码如下(详情见注释):
1
2var remember = false; //记录当前菜单状态,当下次访问时使用
3var contractall_default= 1; //系统菜单项状态1:只显示第一项2:展开所有项 3:收缩所有的菜单项
4
5var menu, titles, submenus, arrows, bypixels; //定义指定的菜单数组变量
6var heights = new Array();
7var speed=10;//加载菜单项的速度
8
9var n = navigator.userAgent;
10
11if(/Opera/.test(n))
12{
13bypixels = 2;
14}
15else if(/Firefox/.test(n))
16{
17bypixels = 3;
18}
19else if(/MSIE/.test(n))
20{
21 bypixels = 2;
22}
23
24
25//展开所有菜单项
26function slash_expandall()
27{
28if (typeof menu!="undefined")
29{
30 for(i=0; i<Math.max(titles.length, submenus.length); i++)
31 {
32titles.className="title";
33arrows.src = imgpath+"/top_level_ico1.gif";
34submenus.style.display="";
35submenus.style.height = heights+"px";
36 }
37}
38}
39
40
41//收缩所有菜单项
42function slash_contractall()
43{
44if (typeof menu!="undefined")
45{
46 for(i=0; i<Math.max(titles.length, submenus.length); i++)
47 {
48titles.className="titlehidden";
49arrows.src = imgpath+"/top_level_ico2.gif";
50submenus.style.display="none";
51submenus.style.height = 0;
52 }
53}
54}
55
56
57
58//初始化函数
59function init(){
60menu = getElementsByClassName("sdmenu", "div", document)[0];
61titles = getElementsByClassName("title", "span", menu);
62submenus = getElementsByClassName("submenu", "div", menu);
63arrows = getElementsByClassName("arrow", "img", menu);
64for(i=0; i<Math.max(titles.length, submenus.length); i++)
65{
66titles.onclick = gomenu;
67arrows.onclick = gomenu;
68heights = submenus.offsetHeight;
69submenus.style.height = submenus.offsetHeight+"px";
70/*alert(i); */
71
72if(i>0)
73{
74 titles.className="titlehidden";
75arrows.src = imgpath+"/top_level_ico2.gif";
76submenus.style.display="none";
77submenus.style.height = 0;
78//alert('123');
79 }
80 }
81
82if(remember)
83 {
84 restore();
85 }
86
87 //根据菜单项状态设置,显示菜单
88 switch(contractall_default)
89 {
90 case 1:
91 {
92 break;
93 }
94 case 2:
95 {
96 slash_expandall();break;
97 }
98 case 3:
99 {
100 slash_contractall();
101 }
102 default:
103 {
104 break;
105 }
106 }
107}
108
109//存储菜单项状态
110function restore() {
111if(getcookie("menu") != null) {
112var hidden = getcookie("menu").split(",");
113for(var i in hidden) {
114
115titles[hidden].className = "titlehidden";
116submenus[hidden].style.height = "0px";
117submenus[hidden].style.display = "none";
118arrows[hidden].src =imgpath+"/top_level_ico2.gif";
119 }
120}
121}
122
123//定向到指定的菜单项进行相应操作
124function gomenu(e)
125{
126if (!e)
127{
128 e = window.event;
129}
130
131var ce = (e.target) ? e.target : e.srcElement;
132
133var sm;
134
135//找到当前菜单项在数组中的位置,用于下面显示或隐藏判断
136for(var i in titles)
137{
138if(titles == ce || arrows == ce)
139{
140sm = i;
141}
142}
143
144//当前菜单项是展示状态时
145if(parseInt(submenus[sm].style.height) > parseInt(heights[sm])-2)
146{
147hidemenu(sm);
148}
149else if(parseInt(submenus[sm].style.height) < 2)//当是收缩状态
150{
151titles[sm].className = "title";
152
153//当菜单只能展开一项(其余菜单项须全部收起)
154if(contractall_default ==1)
155{
156slash_contractall();
157}
158//显示指定的菜单项
159showmenu(sm);
160}
161}
162
163//隐藏指定的菜单元素
164function hidemenu(sm)
165{
166var nr = submenus[sm].getElementsByTagName("a").length*bypixels+speed;
167submenus[sm].style.height = (parseInt(submenus[sm].style.height)-nr)+"px";
168var to = setTimeout("hidemenu("+sm+")", 5);
169
170if(parseInt(submenus[sm].style.height) <= nr)
171{
172clearTimeout(to);
173submenus[sm].style.display = "none";
174submenus[sm].style.height = "0px";
175arrows[sm].src = imgpath+"/top_level_ico2.gif";
176titles[sm].className = "titlehidden";
177}
178}
179
180//显示指定的菜单元素
181function showmenu(sm)
182{
183var nr = submenus[sm].getElementsByTagName("a").length*bypixels+speed;
184submenus[sm].style.display = "";
185submenus[sm].style.height = (parseInt(submenus[sm].style.height)+nr)+"px";
186var to = setTimeout("showmenu("+sm+")", 30);
187if(parseInt(submenus[sm].style.height) > (parseInt(heights[sm])-nr))
188{
189clearTimeout(to);
190submenus[sm].style.height = heights[sm]+"px";
191arrows[sm].src = imgpath+"/top_level_ico1.gif";
192}
193}
194
195//保存菜单元素
196function store()
197{
198var hidden = new Array();
199for(var i in titles)
200{
201if(titles.className == "titlehidden")
202{
203hidden.push(i);
204}
205}
206putcookie("menu", hidden.join(","), 5);
207}
208
209//获取指定样式的元素
210function getElementsByClassName(strClassName, strTagName, oElm){
211var arrElements = (strTagName == "*" && document.all)? document.all : oElm.getElementsByTagName(strTagName);
212var arrReturnElements = new Array();
213strClassName = strClassName.replace(/\-/g, "\\-");
214var oRegExp = new RegExp("(^|\\s)" + strClassName + "(\\s|$)");
215var oElement;
216for(var i=0; i<arrElements.length; i++)
217{
218oElement = arrElements;
219if(oRegExp.test(oElement.className))
220{
221arrReturnElements.push(oElement);
222}
223}
224return (arrReturnElements)
225}
226
227function putcookie(c_name,value,expiredays)
228{
229var exdate=new Date();
230exdate.setDate(exdate.getDate()+expiredays);
231document.cookie = c_name + "=" + escape(value) + ((expiredays==null) ? "" : ";expires="+exdate);
232}
233
234function getcookie(c_name)
235{
236if(document.cookie.length > 0)
237{
238var c_start = document.cookie.indexOf(c_name + "=");
239if(c_start != -1)
240{
241c_start = c_start + c_name.length + 1;
242var c_end = document.cookie.indexOf(";",c_start);
243if(c_end == -1)
244{
245c_end = document.cookie.length;
246}
247return unescape(document.cookie.substring(c_start, c_end));
248}
249}
250return null;
251}
252
253window.onload = init;
254
255
其余的大家可以详细看一下包中的相关内容即可, 这里就不再多说了:)

本文标签:

很赞哦! ()

相关源码

  • (自适应)绿色LED灯具照明灯饰灯光灯泡pbootcms网站源码下载本模板基于PbootCMS内核开发,为LED照明、灯具制造及相关光电技术企业量身打造。设计充分考虑了照明行业的展示需求,从产品陈列到技术说明,从光源展示到工程案例,每一个细节都体现出专业照明行业的特点。查看源码
  • 帝国CMS7.5养生生活健康网模板完整带会员中心可封装APP本套模板为生活服务类网站设计,适用于两性健康、减肥瘦身、生活资讯等领域。采用帝国CMS7.5核心开发,结构清晰合理,视觉体验舒适,能够有效满足相关行业的建站需求。查看源码
  • (自适应响应式)个人作品技术文章博客网站模板下载基于PbootCMS内核开发的响应式博客模板,为数字营销、技术分享类内容打造。采用前沿设计理念,兼顾内容展示与阅读体验,适配各类终端设备。通过本模板可快速构建专业级行业博客,有效传播专业知识与案例成果。查看源码
  • (自适应)个人图集图片相册画册pbootcms网站模板源码本模板基于PbootCMS系统开发,为图片展示类网站设计,特别适合个人作品集、摄影画册、艺术图集等内容展示。采用响应式布局技术,确保各类图片在不同设备上查看源码
  • (PC+WAP)五金机械设备营销型模板下载带在线留言为机械设备制造商设计的营销型模板,集成产品参数对比系统、产品展示模块和询价管理功能。采用PbootCMS开发内核,PHP7+运行环境。手工编写语义化HTML5结构,CSS3动画优化交互体验。查看源码
  • (自适应响应式)英文外贸医疗科研耗材设备pbootcms网站模板为医疗设备和外贸企业设计的响应式网站模板,基于PbootCMS系统开发。突出医疗产品认证展示和国际化特性,通过专业化的产品参数展示模块和文档管理系统,满足医疗行业严格的信息披露要求。查看源码
分享笔记 (共有 篇笔记)
验证码:

本栏推荐