Discuz!NT控件剖析 之 左侧导航控件


本文示例源代码或素材下载

其实这个控件的核心基本都在JS上,而相关的数据绑定和显示却非常简单。而需要说明的是在Discuz!NT的1.0和2.0正式版,这个控件做过一些调整,当然改动也基本上是在JS上,今天给大家的源码是1.0正式版的代码,虽然有些“旧”,但程序本身的思想没变,大家只要明白了这里的源码,有了这碗酒垫底,相信再看即将开源的2.0代码,就会一目了然了。

  好了,废话到此,马上开始今天的话题!

  先请大家看一下这个控件运行时的效果图:

  效果图1:

  

  效果图2:

  

  首先将相应的C#代码放出来:

 1Property ScriptPath#region Property ScriptPath
 2
 3  /**//// <summary>
 4  /// Javascript脚本文件所在目录。
 5  /// </summary>
 6  [Description("Javascript脚本文件所在目录。"),DefaultValue("./")]
 7  public string ScriptPath
 8  {
 9 get
10 {
11 object obj = ViewState["NavMenuScriptPath"];
12 return obj == null ? "js/Navbar.js" :(string) obj;
13 }
14 set
15 {
16 ViewState["NavMenuScriptPath"] = value;
17 }
18  }
19
20  #endregion
21
22
23  Property ImageUrl#region Property ImageUrl
24  [Bindable(true), Category("Appearance"), DefaultValue("")]
25  public string ImageUrl
26  {
27 get
28 {
29 if (base.ViewState["NavMenuimageurl"] != null)
30 {
31  return (String)base.ViewState["NavMenuimageurl"];
32 }
33 else
34 {
35  return "images/";//String.Empty;
36 }
37 }
38 set
39 {
40 base.ViewState["NavMenuimageurl"] = value;
41 }
42  }
43
44  #endregion
45
46
47  Property CssPath#region Property CssPath
48
49  /**//// <summary>
50  /// Css文件所在目录。
51  /// </summary>
52  [Description("Javascript脚本文件所在目录。"),DefaultValue("./")]
53  public string CssPath
54  {
55 get
56 {
57 object obj = ViewState["NavMenuCssPath"];
58 return obj == null ? "styles/nav.css" :(string) obj;
59 }
60 set
61 {
62 ViewState["NavMenuCssPath"] = value;
63 }
64  }
65
66  #endregion
67
68
69  Property XmlFileFullPathName#region Property XmlFileFullPathName
70
71  /**//// <summary>
72  /// Xml文件所在目录。
73  /// </summary>
74  [Description("Xml文件所在目录。"),DefaultValue("./")]
75  public string XmlFileFullPathName
76  {
77 get
78 {
79 object obj = ViewState["NavMenuXmlFileFullPathName"];
80 return obj == null ? "xml/navmenu.xml" :(string) obj;
81 }
82 set
83 {
84 ViewState["NavMenuXmlFileFullPathName"] = value;
85 }
86  }
87
88  #endregion
89
90  protected override void OnPreRender(EventArgs e)#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>
95  protected override void OnPreRender(EventArgs e)
96  {
97    StringBuilder sb = new StringBuilder();
98
99    sb.Append("<script type="text/javascript" src="" + this.ScriptPath + ""></script>rn");
100    sb.Append("<link rel="stylesheet" type="text/css" href="" + this.CssPath + "" />rn");
101    sb.Append("<script type="text/javascript">var imgpath='" + this.ImageUrl + "';</script>rn");
102    sb.Append("<script language="javascript" src="" + this.ScriptPath + ""></script>rn");
103#if NET1
104      if (!Page.IsClientScriptBlockRegistered("NavMenu"))
105      {
106        Page.RegisterClientScriptBlock("NavMenu", sb.ToString());
107      }
108#else
109      if (!Page.ClientScript.IsClientScriptBlockRegistered("NavMenu"))
110      {
111        Page.ClientScript.RegisterClientScriptBlock(this.GetType(), "NavMenu", sb.ToString());
112      }
113#endif
114
115      base.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    
127 output.Write("<div class="sdmenu">rn");
128 
129 System.Data.DataSet dsSrc = new System.Data.DataSet();
130 dsSrc.ReadXml(Page.Server.MapPath(this.XmlFileFullPathName));
131
132 int count=0;
133 foreach(System.Data.DataRow dr in dsSrc.Tables[0].Rows)
134 {
135  输出主菜单#region 输出主菜单
136  output.Write(" <table>rn");
137  output.Write("<tr>rn");
138  if(count==0)
139  {
140  output.Write("<td width="196px"><span class="title" id="top">"+
141   dr["menutitle"]+"</span></td>rn");
142  }
143  else
144  {
145  output.Write("<td width="196px"><span class="title">"+
146   dr["menutitle"]+"</span></td>rn");
147  }
148  output.Write("<td><img src=""+this.ImageUrl+"/top_level_ico1.gif"
149   class="arrow" /></td>rn");
150  output.Write("</tr>rn");
151  output.Write("</table>rn");
152  #endregion
153
154  输出子菜单#region 输出子菜单
155  output.Write(" <div class="submenu">rn");
156  output.Write("<table>rn");
157  foreach(System.Data.DataRow drs in dsSrc.Tables[1].Select("menuparentid='"+
158    dr["menuid"]+"'"))
159  {
160  
161  output.Write("<tr>rn");
162  if(drs["imgurl"].ToString().Trim()!="")
163  {
164   output.Write(" <td class="lefttd" ><img src=""+
165   drs["imgurl"].ToString().Trim()+""
166   class="submenuimg" align="absmiddle"/></td>");
167  }
168  else
169  {
170   output.Write(" <td class="lefttd" ></td>");
171  }
172  if((drs["frameid"].ToString().Trim()=="top")||(drs["frameid"].ToString().
173   Trim()==""))
174  {
175   output.Write("<td><a href="javascript:void(0);"
176   onclick="javascript:top.location.href='"+
177   drs["link"].ToString().Trim()+"';"
178   onfocus="this.blur();">"+
179   drs["menutitle"].ToString().Trim()+"</a></td>rn");
180  }
181  else
182  {
183   output.Write("<td><a href=""+drs["link"].ToString().Trim()+""
184   target="+drs["frameid"].ToString().Trim()+"
185   onfocus="this.blur();">"+
186   drs["menutitle"].ToString().Trim()+"</a></td>rn");
187  }
188  output.Write("</tr>rn");
189  }
190  output.Write("</table>rn");
191  output.Write("</div>rn");
192  #endregion
193
194  //打印分割符
195  output.Write("<div class="splitter">&nbsp;</div>rn");
196  count++;
197  }
198
199  output.Write("</div>rn"); 
200 }
201 }
202
203  
204
205上面的代码因为太简单,就不多做介绍了。而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{
13  bypixels = 2;
14}
15else if(/Firefox/.test(n))
16{
17  bypixels = 3;
18}
19else if(/MSIE/.test(n))
20{
21  bypixels = 2;
22}
23
24
25//展开所有菜单项
26function slash_expandall()
27{
28  if (typeof menu!="undefined")
29  {
30   for(i=0; i<Math.max(titles.length, submenus.length); i++)
31   {
32   titles[i].className="title";
33   arrows[i].src = imgpath+"/top_level_ico1.gif";
34   submenus[i].style.display="";
35   submenus[i].style.height = heights[i]+"px";
36   }
37  }
38}
39
40
41//收缩所有菜单项
42function slash_contractall()
43{
44  if (typeof menu!="undefined")
45  {
46   for(i=0; i<Math.max(titles.length, submenus.length); i++)
47   {
48   titles[i].className="titlehidden";
49   arrows[i].src = imgpath+"/top_level_ico2.gif";
50   submenus[i].style.display="none";
51   submenus[i].style.height = 0;
52   }
53  }
54}
55
56
57
58//初始化函数
59function init(){
60  menu = getElementsByClassName("sdmenu", "div", document)[0];
61  titles = getElementsByClassName("title", "span", menu);
62  submenus = getElementsByClassName("submenu", "div", menu);
63  arrows = getElementsByClassName("arrow", "img", menu);
64  for(i=0; i<Math.max(titles.length, submenus.length); i++)
65  {
66    titles[i].onclick = gomenu;
67    arrows[i].onclick = gomenu;
68    heights[i] = submenus[i].offsetHeight;
69    submenus[i].style.height = submenus[i].offsetHeight+"px";
70    /**//*alert(i); */
71
72    if(i>0)
73    {
74      titles[i].className="titlehidden";
75    arrows[i].src = imgpath+"/top_level_ico2.gif";
76    submenus[i].style.display="none";
77    submenus[i].style.height = 0;
78    //alert('123');
79   }
80 }
81
82  if(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() {
111  if(getcookie("menu") != null) {
112    var hidden = getcookie("menu").split(",");
113    for(var i in hidden) {
114     
115      titles[hidden[i]].className = "titlehidden";
116      submenus[hidden[i]].style.height = "0px";
117      submenus[hidden[i]].style.display = "none";
118      arrows[hidden[i]].src =imgpath+"/top_level_ico2.gif";
119     }
120  }
121}
122
123//定向到指定的菜单项进行相应操作
124function gomenu(e)
125{
126  if (!e)
127  {
128    e = window.event;
129  } 
130  
131  var ce = (e.target) ? e.target : e.srcElement;
132  
133  var sm;
134  
135  //找到当前菜单项在数组中的位置,用于下面显示或隐藏判断
136  for(var i in titles)
137  {
138    if(titles[i] == ce || arrows[i] == ce)
139    {
140      sm = i;
141    }
142  }
143
144  //当前菜单项是展示状态时
145  if(parseInt(submenus[sm].style.height) > parseInt(heights[sm])-2)
146  {
147    hidemenu(sm);
148  }
149  else if(parseInt(submenus[sm].style.height) < 2) //当是收缩状态
150  {
151    titles[sm].className = "title";
152    
153    //当菜单只能展开一项(其余菜单项须全部收起)
154    if(contractall_default ==1)
155    {
156      slash_contractall();
157    }
158    //显示指定的菜单项
159    showmenu(sm);
160  }
161}
162
163//隐藏指定的菜单元素
164function hidemenu(sm)
165{
166  var nr = submenus[sm].getElementsByTagName("a").length*bypixels+speed;
167  submenus[sm].style.height = (parseInt(submenus[sm].style.height)-nr)+"px";
168  var to = setTimeout("hidemenu("+sm+")", 5);
169
170  if(parseInt(submenus[sm].style.height) <= nr)
171  {
172    clearTimeout(to);
173    submenus[sm].style.display = "none";
174    submenus[sm].style.height = "0px";
175    arrows[sm].src = imgpath+"/top_level_ico2.gif";
176    titles[sm].className = "titlehidden";
177  }
178}
179
180//显示指定的菜单元素
181function showmenu(sm)
182{
183  var nr = submenus[sm].getElementsByTagName("a").length*bypixels+speed;
184  submenus[sm].style.display = "";
185  submenus[sm].style.height = (parseInt(submenus[sm].style.height)+nr)+"px";
186  var to = setTimeout("showmenu("+sm+")", 30);
187  if(parseInt(submenus[sm].style.height) > (parseInt(heights[sm])-nr))
188  {
189    clearTimeout(to);
190    submenus[sm].style.height = heights[sm]+"px";
191    arrows[sm].src = imgpath+"/top_level_ico1.gif";
192  }
193}
194
195//保存菜单元素
196function store()
197{
198  var hidden = new Array();
199  for(var i in titles)
200  {
201    if(titles[i].className == "titlehidden")
202    {
203      hidden.push(i);
204    }
205  }
206  putcookie("menu", hidden.join(","), 5);
207}
208
209//获取指定样式的元素
210function getElementsByClassName(strClassName, strTagName, oElm){
211  var arrElements = (strTagName == "*" && document.all)? document.all : oElm.getElementsByTagName(strTagName);
212  var arrReturnElements = new Array();
213  strClassName = strClassName.replace(/-/g, "-");
214  var oRegExp = new RegExp("(^|s)" + strClassName + "(s|$)");
215  var oElement;
216  for(var i=0; i<arrElements.length; i++)
217  {
218    oElement = arrElements[i];   
219    if(oRegExp.test(oElement.className))
220    {
221      arrReturnElements.push(oElement);
222    } 
223  }
224  return (arrReturnElements)
225}
226
227function putcookie(c_name,value,expiredays)
228{
229  var exdate=new Date();
230  exdate.setDate(exdate.getDate()+expiredays);
231  document.cookie = c_name + "=" + escape(value) + ((expiredays==null) ? "" : ";expires="+exdate);
232}
233
234function getcookie(c_name)
235{
236  if(document.cookie.length > 0)
237  {
238    var c_start = document.cookie.indexOf(c_name + "=");
239    if(c_start != -1)
240    {
241      c_start = c_start + c_name.length + 1;
242      var c_end = document.cookie.indexOf(";",c_start);
243      if(c_end == -1)
244      {
245        c_end = document.cookie.length;
246      }
247      return unescape(document.cookie.substring(c_start, c_end));
248    }
249  }
250  return null;
251}
252
253window.onload = init;
254
255


  其余的大家可以详细看一下包中的相关内容即可, 这里就不再多说了:)

  好了,主要是东西就先交待到这里了。如果大家有什么问题或建议,欢迎与我交流,我的邮件是daizhj@discuz.com

本文作者:
« 
» 
快速导航

Copyright © 2016 phpStudy | 豫ICP备2021030365号-3