.container1 { height: 30px; }.container1 div { float: left; }.container1 div, .container1_2 div { width: 100px; background: none repeat scroll 0% 0% rgb(250, 252, 253); border: 1px solid rgb(92, 156, 192); padding: 10px; }div.on1 { font-weight: bold; background: none repeat scroll 0% 0% rgb(238, 243, 247); }div.on1_2 { font-weight: bold; background: none repeat scroll 0% 0% rgb(255, 255, 247); border: 1px solid rgb(255, 204, 0); } 菜單使用演示:
效果預覽
自定義樣式
下拉菜單
任意定位
相對容器
相對菜單
-------------test-------------
位置: 第四個第三個第二個第一個1秒0.5秒0.2秒不延時
仿京東商城商品分類菜單:
.container2, .container2 dd, .container2_2 dl, .container2_2 dd { margin: 0pt; }.container2 { font-size: 14px; width: 190px; border: 1px solid rgb(207, 32, 32); background: none repeat scroll 0% 0% rgb(255, 255, 245); padding: 5px 8px; line-height: 30px; color: rgb(51, 51, 51); }.container2 dt { font-weight: bold; color: rgb(207, 32, 32); }.container2 dd { background: url("") no-repeat scroll 180px 10px transparent; }.container2_2 { background-color: rgb(190, 190, 195); display: none; }.container2_2 dl { font-size: 14px; width: 200px; border: 1px solid rgb(150, 150, 150); background: none repeat scroll 0% 0% rgb(255, 255, 255); position: relative; left: -3px; top: -3px; }.container2_2 dd div { padding: 5px 20px; background: url("") no-repeat scroll 6px 7px transparent; }.container2_2 dt, .shadow { padding: 0pt 5px; position: absolute; background: none repeat scroll 0% 0% rgb(255, 255, 255); border-width: 1px 0pt 1px 1px; border-style: solid none solid solid; border-color: rgb(150, 150, 150) -moz-use-text-color rgb(150, 150, 150) rgb(150, 150, 150); width: 169px; left: -180px; top: -1px; height: 24px; line-height: 24px; }.shadow { background-color: rgb(190, 190, 195); border-color: rgb(190, 190, 195); top: 0pt; }.container2_2 a { display: block; }.container2_2 a:link, .container2_2 a:visited, .container2_2 a:active { color: rgb(51, 51, 51); text-decoration: none; }.container2_2 a:hover { color: rgb(255, 96, 38); text-decoration: underline; }
仿window xp右鍵菜單:
.container3 { font-size: 12px; border: 1px solid rgb(157, 157, 161); padding: 3px; line-height: 18px; background: none repeat scroll 0% 0% rgb(255, 255, 255); cursor: default; -moz-user-select: none; }.container3 div { padding: 0pt 20px; }.menu3_1 { color: rgb(172, 168, 153); }.menu3_2 { background: url("") no-repeat scroll 133px 0pt transparent; }.menu3_2_on { background-position: 133px -18px; }.menu3_3 { background: url("") no-repeat scroll left -36px transparent; }.menu3_3_on { background-position: left -54px; }.menu3_4 { background: url("") no-repeat scroll left -72px transparent; }.menu3_4_on { background-position: left -90px; }.line3 { border-bottom: 1px solid rgb(172, 168, 153); margin: 4px 0pt; }.on3 { background-color: rgb(49, 106, 197); color: rgb(255, 255, 255); }.area3 { width: 500px; height: 200px; border: 1px solid rgb(172, 168, 153); }.pos3 { position: absolute; display: none; width: 150px; }
V
I
E
F
P
S
P
S
仿淘寶拼音索引菜單:
.container4 li, .container4_2 li { list-style: none outside none; }.container4 ul, .container4_2 { margin: 0pt; }.container4 { width: 350px; padding: 7px 10px; font: 12px/15px Verdana; border: 1px solid rgb(204, 204, 204); background: none repeat scroll 0% 0% rgb(255, 254, 237); height: 15px; }.container4 li { float: left; padding: 0pt 10px; border-right: 1px solid rgb(204, 204, 204); }.container4 div { float: left; color: rgb(0, 0, 0); padding-right: 10px; }li.menu4 { position: relative; margin-left: -1px; top: -1px; z-index: 9999; border-width: 1px 1px 0pt; border-style: solid solid none; border-color: rgb(133, 204, 255) rgb(133, 204, 255) -moz-use-text-color; padding-bottom: 8px; color: rgb(255, 96, 38); background: none repeat scroll 0% 0% rgb(219, 243, 255); }.container4_2 { width: 350px; padding: 10px; border: 1px solid rgb(133, 204, 255); background: none repeat scroll 0% 0% rgb(219, 243, 255); line-height: 25px; font-size: 14px; font-weight: bold; display: none; }.container4_2 a { display: block; }.container4_2 a:link, .container4_2 a:visited, .container4_2 a:active { color: rgb(86, 85, 83); text-decoration: none; }.container4_2 a:hover { color: rgb(255, 85, 0); text-decoration: underline; }.container4 a:link, .container4 a:visited, .container4 a:hover, .container4 a:active { color: rgb(86, 85, 83); text-decoration: none; }.menu4 a:link, .menu4 a:visited, .menu4 a:active { color: rgb(255, 96, 38); }.menu4 a:hover { color: rgb(255, 96, 38); text-decoration: underline; }
程序原理
程序最關鍵的地方是多級聯動,先大概説明一下:
首先第一級的菜單元素整理好後,從他們開始,當某個菜單元素觸發顯示下級菜單時,
準備好下一級的容器元素,並把下一級的菜單元素放進去,再定位並顯示容器元素。
裏面的菜單元素又可以觸發顯示下級菜單,然後按上面的步驟執行下去。
這樣一級一級的遞推下去,形成多級的聯動菜單。
程序説明
【容器對象】
在多級聯動中,每一級都需要一個容器元素來存放菜單元素。
程序中每個容器元素都對應一個容器對象,用來記錄該容器的相關信息。
容器對象的集合記錄在程序的_containers屬性中。
容器參數containers是程序實例化時的必要參數,它的結構如下:
[
容器元素(id),
{ id: 容器元素(id), menu: 插入菜單元素(id) },
]
首先如果containers不是數組的話,程序會自動轉成單元素數組。
如果菜單插入的元素就是容器元素本身,可以直接用容器元素(id)作為數組元素。
否則應該使用一個對象結構,它包括一個id屬性表示是容器元素(id)和一個menu屬性表示菜單插入的元素(id)。
containers會在程序初始化時這樣處理:
Code
$$A.forEach($$A.isArray(containers) ? containers : [containers], function (o, i){
var pos, menu;
if ( o.id ) {
pos = o.id; menu = o.menu ? o.menu : pos;
} else {
pos = menu = o;
};
pos = $$(pos); menu = $$(menu);
pos && menu && this ._iniContainer( i, { " pos " : pos, " menu " : menu } );
}, this );
主要是生成一個容器對象,其中pos屬性是容器元素,menu屬性是插入菜單的元素。
然後傳遞索引和容器對象給_iniContainer函數,對容器對象做初始化。
在_iniContainer中,首先用_resetContainer重置容器對象可能在程序中設置過的屬性。
再給容器元素添加事件:
Code
$$E.addEvent(oContainer, " mouseover " , $$F.bind( function (){ clearTimeout( this ._timerContainer); }, this ));
$$E.addEvent(oContainer, " mouseout " , $$F.bindAsEventListener( function (e){
// 先判斷是否移出到所有容器之外
var elem = e.relatedTarget,
isOut = $$A.every( this ._containers, function (o){ return o.pos == elem || ! ($$D.contains(o.pos, elem)); });
if ( isOut ) {
// 清除定時器並隱藏
clearTimeout( this ._timerContainer); clearTimeout( this ._timerMenu);
this ._timerContainer = setTimeout( $$F.bind( this .hide, this ), this .delay );
};
}, this ));
在mouseout時,先判斷是否容器內部或容器之間觸發,不是的話再用定時器執行hide隱藏函數。
在hide裏面,主要是隱藏容器:
this ._forEachContainer(
function
(o, i){
if
( i
===
0
) {
this
._resetCss(o);
} else
{
this
._hideContainer(o);
};
});
由於第一級容器一般是不自動隱藏的,只需要用_resetCss來重置樣式。
其他容器會用_hideContainer函數來處理隱藏:
$$D.setStyle( container.pos, { left: "
-9999px
"
, top:
"
-9999px
"
, visibility:
"
hidden
"
} );
this
._containers[container._index
-
1
]._active
=
null
;
其中_active屬性是保存該容器觸發下級菜單的菜單對象,在隱藏容器同時重置上一級容器的_active。
在mouseover時清除容器定時器,其實就是取消hide執行。
之後是設置樣式:
if ( index ) {
$$D.setStyle(container.pos, {
position: "
absolute
"
, display:
"
block
"
, margin:
0
,
zIndex: this
._containers[index
-
1
].pos.style.zIndex
+
1
});
};
除了第一級容器外,都設置浮動需要的樣式。
最後用_index屬性記錄索引,方便調用,並把容器對象插入到容器集合中:
container._index =
index;
this
._containers[index]
=
container;
這個索引很重要,它決定了容器是用在第幾級菜單。
【菜單對象】
容器元素插入了菜單元素才算一個菜單。
程序中每個菜單元素都對應一個菜單對象,用來記錄該菜單的相關信息。
程序初始化前,應該先創建好自定義菜單集合,它的結構是這樣的:
[
{ id: 1
, parent:
0
, html: 元素內容 },
{ id: 2
, parent:
1
, html: 元素內容 },
]
其中id是菜單的唯一標識,parent是父級菜單的id。
除了這兩個關鍵屬性外,還可以包括以下屬性:
rank:排序屬性
elem:自定義元素
tag:生成標籤
css:默認樣式
hover:觸發菜單樣式
active: 顯示下級菜單時顯示樣式
html:菜單內容
relContainer:是否相對容器定位(否則相對菜單)
relative:定位對象
attribute:自定義Attribute屬性
property:自定義Property屬性
其中relContainer和relative是用於下級容器定位的。
自定義菜單集合會保存在_custommenu屬性中。
在程序初始化時會執行_buildMenu程序,根據這個_custommenu生成程序需要的_menus菜單對象集合。
_buildMenu是比較關鍵的程序,菜單的層級結構就是在這裏確定,它由以下幾步組成:
第一步,清除舊菜單對象集合的dom元素。
這一步後面“內存泄漏”會詳細説明。
第二步,生成菜單對象集合。 為了能更有效率地獲取指定id的菜單對象,_menus是以id作為字典關鍵字的對象。
首先創建帶根菜單(id為“0”)對象的_menus:
this ._menus =
{
"
0
"
: {
"
_children
"
: [] } };
然後整理_custommenu並插入到_menus中:
Code
$$A.forEach( this ._custommenu, function (o) {
var menu = $$.deepextend( $$.deepextend( {}, options ), o || {} );
if ( !! this ._menus[ menu.id ] ) { return ; };
menu._children = []; menu._index = - 1 ;
this ._menus[menu.id] = menu;
}, this );
其中菜單對象中包含對象屬性,要用deepextend深度擴展來複制屬性。
為確保id是唯一標識,會排除相同id的菜單,間接排除了id為“0”的菜單。
在重置_children(子菜單集合)和_index(聯級級數)之後,就可以插入到_menus中了。
第三步,建立樹形結構。
菜單之間的關係是一個樹形結構,程序通過id和parent來建立這個關係的(寫過數據庫分級結構的話應該很熟悉)。
而第一版是把子類直接菜單寫在菜單元素的menu屬性中,形成類似多維數組的結構。
比較這兩個方法,第一版的優勢在於定義菜單時就直接確立了關係,而新版還必須根據id和parent來判斷增加代碼複雜度。
新版的優勢是使用維護方便,靈活,級數越多就越體現出來,而第一版剛好相反。
能不能結合這兩個方法的優勢呢?
這裏採用了一個折中的方法,在寫自定義菜單對象時用的是新版的方法,然後程序初始化時把它轉換成類多維數組結構。
轉換過程是這樣的:
首先根據parent找到父菜單對象:
var parent =
this
._menus[o.parent];
如果找不到父菜單對象或父菜單對象就是菜單對象本身的,當成一級菜單處理:
if ( !
parent
||
parent
===
o ) { parent
=
menus[o.parent
=
"
0
"
]; };
最後把當前菜單對象放到父菜單對象的_children集合中:
parent._children.push(o);
這就把_menus變成了類多維數組結構,而且這個結構不會發生死循環。
第四步,整理菜單對象集合。 這步主要是整理_menus裏面的菜單對象。
首先,把自定義菜單元素放到碎片文檔中:
!! o.elem &&
( o.elem
=
$$(o.elem) )
&&
this
._frag.appendChild(o.elem);
菜單元素是需要顯示時才會處理的,這樣可以防止在容器上出現未處理的菜單元素。
然後是修正樣式(詳細看樣式設置部分)。
最後,對菜單對象的_children集合進行排序:
o._children.sort( function ( x, y ) {
return
x.rank
-
y.rank
||
x.id
-
y.id; });
先按rank再按id排序,跟菜單對象定義的順序是無關的。
執行完BuildMenu程序之後,_menus菜單對象集合就建立好了。
麻煩的是在每次修改_custommenu之後,都必須執行一次_buildMenu程序。
【多級聯動】
容器對象和菜單對象都準備好了,下面就是如何利用它們來做程序的核心——多級聯動效果了。
多級聯動包括以下步驟:
第一步,準備一級容器。
一級容器一般是顯示狀態的(也可以自己定義它的顯示隱藏,像仿右鍵菜單那樣)。
第二步,向容器插入菜單。
通過_insertMenu程序,可以向指定容器插入指定菜單,其中第一個參數是索引,第二個參數是父菜單id。
在_insertMenu程序裏面,先判斷是否同一個父級菜單,是的話就返回不用重複操作了:
var container =
this
._containers[index];
if ( container._parent
===
parent ) {
return
; };
container._parent = parent;
接着把原有容器內菜單移到碎片對象中:
$$A.forEach( container._menus, function (o) { o._elem
&&
this
._frag.appendChild(o._elem); },
this
);
在第一版,菜單每次使用都會重新創建,新版改進後會把舊菜單元素保存到碎片對象中,要使用時再拿出來。
然後根據parent獲取父菜單對象,並把父菜單的_children子菜單集合的插入到容器中:
$$A.forEach( this ._menus[parent]._children,
function
( menu, i ){
this ._checkMenu( menu, index );
container._menus.push(menu);
container.menu.appendChild(menu._elem);
}, this );
這樣整個菜單就準備好了。
第三步,添加觸發下級菜單事件。 上面在把菜單插入到容器之前,會先用_checkMenu程序檢查菜單對象。
_checkMenu程序主要是檢測和處理菜單元素。
首先判斷沒有自定義元素,沒有的話就創建一個:
var elem =
menu.elem;
if (
!
elem ) { elem
=
document.createElement(menu.tag); elem.innerHTML
=
menu.html; };
第一版並不能自定義元素,但考慮到seo、漸進增強等,在新版加入了這個功能。
但每次BuildMenu之後會把所有菜單元素包括自定義元素都清除,這個必須留意。然後分別設置property、attribute和className屬性:
$$.extend( elem, menu.property );
var attribute
=
menu.attribute;
for (
var
att
in
attribute) { elem.setAttribute( att, attribute[att] ); };
elem.className = menu.css;
ps:關於property和attribute的區別請看這裏的attribute/property部分 。
然後是關鍵的一步,添加HoverMenu觸發事件程序:
menu._event = $$F.bindAsEventListener(
this
._hoverMenu,
this
, menu );
$$E.addEvent( elem, " mouseover
"
, menu._event );
處理後的元素會保存在菜單對象的_elem屬性中。
第四步,觸發顯示下級菜單事件。
當觸發了顯示下級菜單事件,就會執行_hoverMenu程序。
在_hoverMenu程序裏面,主要是做一些樣式設置,詳細參考後面的樣式設置部分。
然後是用定時器準備執行_showMenu顯示菜單程序。
第五步,整理菜單容器。 在_showMenu程序中,首先是隱藏不需要的容器:
this ._forEachContainer( function
(o, i) { i
>
index
&&
this
._hideContainer(o); } );
然後判斷當前菜單是否有子菜單,當有子菜單時,先用_checkContainer程序檢查下級菜單容器。
_checkContainer程序主要是檢查容器是否存在,不存在的話就自動添加一個:
var pre =
this
._containers[index
-
1
].Pos
,container = pre.parentNode.insertBefore( pre.cloneNode(
false
), pre );
container.id =
""
;
其實就是用cloneNode複製前一個容器,注意要重置id防止衝突。
雖然程序能自動創建菜單,但也要求至少自定義一個容器。
第六步,顯示菜單容器。 在顯示之前,先按第二步向容器插入菜單,最後就是執行_showContainer程序來定位和顯示容器了。
當下一個容器內的菜單觸發顯示下級菜單事件時,會顯示下下級的菜單容器。
程序就是這樣一級一級遞推下去,形成多級聯級效果。
【樣式設置】
樣式設置也是一個重要的部分,不是説要弄出多炫的界面,而是如何使程序能最大限度地靈活地實現那些界面。
菜單對象有三個樣式相關的屬性,分別是:
css:默認樣式
hover:鼠標進入菜單時使用樣式
active:顯示下級菜單時使用樣式
在_buildMenu程序中,會對這些樣式屬性進行整理:
Code
if ( !! o.elem && o.elem.className ) {
o.css = o.elem.className;
} else if ( o.css === undefined ) { o.css = "" ; };
if ( o.hover === undefined ) { o.hover = o.css; };
if ( o.active === undefined ) { o.active = o.hover; };
可以看到,程序會優先使用自定義元素的class,避免被程序設置的默認樣式覆蓋。
空字符串也可能被用來清空樣式,所以要用undefined來判斷是否自定義了樣式。
程序中主要在兩個地方設置樣式:在鼠標移到菜單元素上時(_hoverMenu)和顯示下級菜單時(_showMenu)。
在_hoverMenu程序中,先對每個顯示的容器設置一次樣式:
this ._forEachContainer( function
(o, i){
if ( o.pos.visibility
===
"
hidden
"
) {
return
; };
this ._resetCss(o);
var menu
=
o._active;
if ( menu ) { menu._elem.className
=
menu.active; };
});
由於鼠標可能是在多個容器間移動,所以所有顯示的容器都需要設置。
用_resetCss重置容器樣式後再設置有下級菜單的菜單的樣式為active。
為了方便獲取,容器對象用一個_active屬性來保存當前容器觸發了下級菜單的菜單對象。
然後是設置鼠標所在菜單的樣式:
if ( this
._containers[menu._index]._active
!==
menu ) { elem.className
=
menu.hover; };
為了優先設置active樣式,在當前菜單不是容器的_active時才設置hover樣式。
在_showMenu程序中,首先把顯示下級菜單的菜單對象保存到容器的_active屬性。
再用_resetCss重置當前容器樣式,這個在同級菜單中移動時會有用。
然後再根據當前菜單是否有下級菜單來設置樣式為active或hover。
【內存泄漏】
上面“菜單對象”中説到清除舊菜單對象的dom元素,這個主要是為了防止內存泄漏。
關於內存泄漏也有很多文章,這裏推薦看看Douglas Crockford的“JScript Memory Leaks ”和winter的“瀏覽器中的內存泄露 ”。
下面説説我解決本程序內存泄漏的經過:
首先,通過調用程序的Add和Delete數千次來測試是否有內存泄漏。
怎麼看出來呢?可以找些相關的工具來檢測,或者直接看任務管理器的頁面文件(pf)使用記錄。
結果發現,雖然每個元素都用removeChild移出了dom,但隨着循環的次數增多,pf還是穩步上升。
於是按照Memory Leaks中説的“we must null out all of its event handlers to break the cycles”去掉事件:
removeEvent( elem, " mouseover
"
, o._event );
效果是有了,但不太理想,然後再逐一排除,發現原來是_elem屬性還關聯着元素,結果經過一些操作後,又把元素append到dom上,還重新創建了一個元素。
於是在移除元素後,立即重置_elem和elem屬性:
o._elem = o.elem = null ;
內存泄漏就沒有了,其實這裏也不算是內存泄露了,而是程序設計有問題了。
所以清除dom元素時必須注意:
1,按照Douglas Crockford的建議,移除所有dom元素相關的事件函數;
2,刪除/重置所有關聯dom元素的js對象/屬性。
【cloneNode的bug】
在上面多級聯動中説到,會用cloneNode複製容器,但cloneNode在ie中有一個bug:
在ie用attachEvent給dom元素綁定事件,在cloneNode之後會把事件也複製過去。
而用addEventListener添加的事件就不會,可以在ie和ff測試下面的代碼:
Code
<! DOCTYPE html >
< html >
< body >
< div id = " t " > div < / div>
< script >
var o = document.getElementById( " t " );
if (o.attachEvent){
o.attachEvent( " onclick " , function (){alert( 2 )});
} else {
o.addEventListener( " click " , function (){alert( 2 )}, false );
}
document.body.appendChild(o.cloneNode( true ));
< / script>
< / body>
< / html>
在ie和ff點擊第一個div都會觸發alert,關鍵是第二個div,在ff不會觸發,而ie就會。
當然這個是不是bug還不清楚,或許attachEvent本來就是這樣設計的也説不定。
但第一版就是由於這個bug,而沒有用cloneNode。在找解決方法之前,再擴展這個問題,看看直接添加onclick事件會不會有同樣的bug。
首先測試在元素裏面添加onclick:
<! DOCTYPE html >
< html
>
< body
>
< div id
=
"
t
"
onclick
=
"
alert(1)
"
>
div
<
/
div>
< script
>
var o
=
document.getElementById(
"
t
"
);
document.body.appendChild(o.cloneNode( true ));
< /
script>
< /
body>
< /
html>
結果在ie和ff都會複製事件。
再測試在js添加onclick:
Code
<! DOCTYPE html >
< html >
< body >
< div id = " t " > div < / div>
< script >
var o = document.getElementById( " t " );
o.onclick = function (){alert( 1 )}
document.body.appendChild(o.cloneNode( true ));
< / script>
< / body>
< / html>
結果在ie和ff都不會複製事件,看來只有attachEvent會引起這個bug。
下面是解決方法:
用John Resig在《精通JavaScript》推薦的Dean Edwards寫的addEvent和removeEvent方法 來添加/移除事件。
它的好處就不用説了,而且它能在ie解決上面説到的cloneNode的bug。
因為它的實現原理是在ie用onclick來綁定事件,而上面的測試也證明用onclick綁定的事件是不會被cloneNode複製的。
ps:我對原版的方法做了些修改,方便調用。
【浮動定位】
容器的浮動定位用的是浮動定位提示效果 中的定位方法。
在該文章中已經詳細説明了如何獲取指定的浮動定位座標,這裏做一些補充。
一般來説用getBoundingClientRect配合scrollLeft/scrollTop就能獲得對象相對文檔的位置座標。
測試下面代碼:
Code
<! DOCTYPE html >
< html >
< body style = " padding:1000px 0; " >
< div id = " t1 " style = " border:1px solid; width:100px; height:100px; " >< / div>
< div id = " t2 " >< / div>
< script >
var $$ = function (id) {
return " string " == typeof id ? document.getElementById(id) : id;
};
var b = 0 ;
window.onscroll = function (){
var t = $$( " t1 " ).getBoundingClientRect().top + document.documentElement.scrollTop;
if ( t != b ){ b = t; $$( " t2 " ).innerHTML += t + " <br> " ; }
}
< / script>
< / body>
< / html>
在除ie8外的瀏覽器,t會保持在一個固定值,但在ie8卻會在1008和1009之間變換(用鼠標一格一格滾會比較明顯)。
雖然多數時候還是標準的1008,但原來的效果可能就會被這1px的差距破壞(例如仿京東和仿淘寶的菜單)。
ps:chrome和safari要把documentElement換成body。
為了解決這個問題,只好在ie8的時候用回傳統的offset來取值了(詳細參考代碼)。
至於造成這個問題的原因還沒弄清楚,各位有什麼相關資料的記得告訴我哦。
使用技巧
在仿京東商城商品分類菜單中,實現了一個陰影效果。
原理是這樣的:
底部是一個灰色背景層(陰影),裏面放內容層,然後設置內容層相對定位(position:relative),並做適當的偏移(left:-3px;top:-3px;)。
由於相對定位會保留佔位空間,這樣就能巧妙地做出了一個可自適應大小的背景層(陰影)。
ps:博客園首頁也做了類似的效果,但貌似錯位有些嚴重哦。
仿右鍵菜單效果並不支持opera,因為opera並沒有類似oncontextmenu這樣的事件,要實現的話會很麻煩。
ps:如果想兼容opera的話,可以看看這篇文章“Opera下自定義右鍵菜單的研究 ”。
注意,在oncontextmenu事件中要用阻止默認事件(preventDefault)來取消默認菜單的顯示。
這個效果還做了一個不能選擇的處理,就是拖動它的內容時不會被選擇。
在ff中把樣式-moz-user-select設為none就可以了,而ie、chrome和safari通過在onselectstart返回false來實現相同的效果。
ps:css3有user-select樣式,但貌似還沒有瀏覽器支持。
當然,還有很多不完善的地方,這裏只是做個參考例子,就不深究了。
仿淘寶拼音索引菜單主要體現了a