📄 jquery.mcdropdown.js
字号:
(function (){
/*!
* mcDropdown jQuery Plug-in
*
* Copyright 2008 Giva, Inc. (http://www.givainc.com/labs/)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* Date: 2008-06-12
* Rev: 1.1
*/
$.fn.mcDropdown = function(list, options) {
// track the dropdown object
var dd;
// create a dropdown for each match
this.each(function() {
dd = $.data(this, "mcDropdown");
// we're already a dropdown, return a reference to myself
if( dd ) return false;
new $.mcDropDownMenu(this, list, options);
});
// return either the dropdown object or the jQuery object reference
return dd || this;
};
// set default options
$.mcDropdown = {
version: "1.1",
setDefaults: function(options){
$.extend(defaults, options);
}
};
// set the defaults
var defaults = {
minRows: 8 // specify the minimum rows before creating a new column
, maxRows: 25 // specify the maximum rows in a column
, targetColumnSize: 2 // specify the default target column size (it'll attempt to create this many columns by default, unless the min/max row rules are not being met)
, openFx: "slideDown" // the fx to use for showing the root menu
, openSpeed: 250 // the speed of the openFx
, closeFx: "slideUp" // the fx to use for hiding the root menu
, closeSpeed: 250 // the speed of the closeFx
, hoverOverDelay: 200 // the delay before opening a submenu
, hoverOutDelay: 0 // the delay before closing a submenu
, showFx: "show" // the fx to use when showing a submenu
, showSpeed: 0 // the speed of the showFx
, hideFx: "hide" // the fx to use when closing a submenu
, hideSpeed: 0 // the speed of the hideFx
, dropShadow: true // determine whether drop shadows should be shown on the submenus
, lineHeight: 19 // the base height of each list item (li) this is normally calculated automatically, but in some cases the value can't be determined and you'll need to set it manually
, screenPadding: 10 // the padding to use around the border of the screen -- this is used to make sure items stay on the screen
, allowParentSelect: false // determines if parent items are allowed to be selected (by default only end nodes can be selected)
, delim: ":" // the delimited to use when showing the display string
, valueAttr: "rel" // the attribute that contains the value to use in the hidden field
, click: null // callback that occurs when the user clicks on a menu item
, select: null // callback that occurs when a value is selected
, init: null // callback that occurs when the control is fully initialized
};
// check to see if the browser is IE6
var isIE6 = ($.browser.version && $.browser.version <= 6);
$.mcDropDownMenu = function(el, list, options){
var $self, thismenu = this, $list, $divInput, settings, typedText = "", matchesCache, oldCache, $keylist, $keylistiframe, bInput;
// create a reference to the dropdown
$self = $(el);
// is the field and input element
bInput = $self.is(":input");
// get the settings for this instance
settings = $.extend({}, defaults, options);
// set the default click behavior
if( settings.click == null ) settings.click = function (e, dropdown, settings){ dropdown.setValue(this.attr(settings.valueAttr)); }
// attach window behaviors
$(document)
// Bind a click event to hide all visible menus when the document is clicked
.bind("click", function(e){
// get the target that was clicked
var $target = $(e.target);
// check to make sure the clicked element was inside the list
if( $target.parents().filter(function (){ return this === $list[0]; }).length ){
// check to see if the user can click on parent items
if( !settings.allowParentSelect && $target.is(".mc_parent") ) return false;
if( settings.click != null && settings.click.apply($target, [e, thismenu, settings]) == false ){
return false;
}
}
// close the menu
thismenu.closeMenu();
});
// store a reference to the list, if it's not already a jQuery object make it one
$list = (((typeof list == "object") && !!list.jquery)) ? list : $(list);
// we need to calculate the visual width for each nested list
$list
// move the list way off screen
.css({position: "absolute", top: -10000, left: -10000})
// find all the ul tags
.find("ul")
// add the root ul tag to the array
.andSelf()
// make all the nodes visible
.css("display", "block")
// loop through each node
.each(function (){
var $el = $(this);
// calculate the width of the element -- using clientWidth is 2x as fast as width()
$el.data("width", $el[0].clientWidth);
})
// now that we've gotten the widths, hide all the lists and move them to x:0, y:0
.css({top: 0, left: 0, display: "none"});
// mark the root children items
$list.find("> li").addClass("mc_root");
// add parent class
$("li > ul", $list).parent().addClass("mc_parent");
// create the div to wrap everything in
$divInput = $('<div class="mcdropdown"><a href="#"></a><input type="hidden" name="' + (el.name || el.id) + '" id="' + (el.id || el.name) + '" /></div>')
.appendTo($('<div style="position: relative;"></div>'))
.parent();
// get a reference to the input element and remove it from the DOM
var $input = $self.replaceWith($divInput).attr({id: "", name: ""});
// get a reference to the hidden form field
var $hidden = $divInput.find(":hidden");
// put the input element back in the div.mcdropdown layer
$divInput = $divInput.find(".mcdropdown").prepend($input);
// store a reference to this link select
$.data($hidden[0], "mcDropdown", this);
// update the height of the outer relative div, this allows us to
// correctly anchor the dropdown
$divInput.parent().height($divInput.outerHeight());
// adjust the width of the new input element
$self
.width($self.width() - $("a", $divInput).width())
// make sure we only attach the next events if we're in input element
.filter(":input")
// turn autocomplete off
.attr("autocomplete", "off")
// add key stroke bindings (IE6 requires keydown)
.bind("keypress", checkKeypress)
// prevent user from selecting text
.bind("mousedown", function (e){ $(this).triggerHandler("focus"); e.stopPropagation(); return false; })
// disable context menu
.bind("contextmenu", function (){ return false; })
// select the text when the cursor is placed in the field
.bind("focus", onFocus)
// when the user leaves the text field
.bind("blur", onBlur);
// IE6 doesn't register certain keypress events, so we must catch them during the keydown event
if( $.browser.msie ) $self.bind("keydown", function (e){
// check to see if a key was pressed that IE6 doesn't trigger a keypress event for
if( ",8,9,37,38,39,40,".indexOf("," + e.keyCode + ",") > -1 ) return checkKeypress(e);
});
// attach a click event to the anchor
$("a", $divInput).bind("click", function (e){
thismenu.openMenu(e);
return false;
});
// set the value of the field
this.setValue = function (value, skipCallback){
// update the hidden value
$hidden.val(value);
// get the display name
var name = displayString(value);
// run the select callback (some keyboard entry methods will manage this callback manually)
if( settings.select != null && skipCallback != true ) settings.select.apply(thismenu, [value, name]);
// update the display value and return the jQuery object
return $self[bInput ? "val" : "text"](name);
};
// set the default value (but don't run callback)
if( bInput ) this.setValue($self.attr("defaultValue"), true);
// get the value of the field (returns array)
this.getValue = function (value){
return [$hidden.val(), $self[bInput ? "val" : "text"]()];
};
// open the menu programmatically
this.openMenu = function (e){
// if the menu is open, kill processing
if( $list.is(":visible") ){
// on a mouse click, close the menu, otherwise just cancel
return (!!e) ? thismenu.closeMenu() : false;
}
function open(){
// columnize the root list
columnizeList($list).hide();
// add the bindings to the menu
addBindings($list);
// anchor the menu relative parent
anchorTo($divInput.parent(), $list);
// remove existing hover classes, which might exist from keyboard entry
$list.find(".mc_hover").removeClass("mc_hover");
// show the menu
$list[settings.openFx](settings.openSpeed, function (){
// scroll the list into view
scrollToView($list);
});
// if the bgIframe exists, use the plug-in
if( isIE6 && !!$.fn.bgIframe ) $list.bgIframe();
}
// if this is triggered via an event, just open the menu
if( e ) open();
// otherwise we need to open the menu asynchronously to avoid collision with $(document).click event
else setTimeout(open, 1);
};
// close the menu programmatically
this.closeMenu = function (e){
// hide any open menus
$list.find("ul:visible").parent().each(function (){
hideBranch.apply(this);
});
// remove the bindings
removeBindings($list);
// close the menu
$list[settings.closeFx](settings.closeSpeed);
};
function getNodeText($el){
// return either an empty string or the node's value
return $el.contents()[0] ? $.trim($el.contents()[0].nodeValue) : "";
};
function getTreePath($li){
if( $li.length == 0 ) return [];
var name = [getNodeText($li)];
// loop through the parents and get the value
$li.parents().each(function (){
var $el = $(this);
// break when we get to the main list element
if( this === $list[0] ) return false;
else if( $el.is("li") ) name.push(getNodeText($el));
});
// return the display name
return name.reverse();
};
function displayValue(value){
// return the path as an array
return getTreePath(getListItem(value));
};
function displayString(value){
// return the display name
return displayValue(value).join(settings.delim);
};
function parseTree($selector){
var s = [], level = (arguments.length > 1) ? ++arguments[1] : 1;
// loop through all the children and store information about the tree
$("> li", $selector).each(
function (){
// get a reference to the current object
var $self = $(this);
// look for a ul tag as a direct child
var $ul = $("> ul", this);
// push a reference to the element to the tree array
s.push({
// get the name of the node
name: getNodeText($self)
// store a reference to the current element
, element: this
// parse and store any children items
, children: ($ul.length) ? parseTree($ul, level) : []
});
}
);
return s;
};
function addBindings(el){
removeBindings(el);
$("> li", el)
.bind("mouseover", hoverOver)
.bind("mouseout", hoverOut);
};
function removeBindings(el){
$("> li", el)
.unbind("mouseover", hoverOver)
.unbind("mouseout", hoverOut);
};
// scroll the current element into view
function scrollToView($el){
// get the current position
var p = position($el);
// get the screen dimensions
var sd = getScreenDimensions();
// if we're hidden off the bottom of the page, move up
if( p.bottom > sd.y ){
$("html,body").animate({"scrollTop": "+=" + ((p.bottom - sd.y) + settings.screenPadding) + "px" })
}
};
function hoverOver(e){
var self = this;
var timer = $.data(self, "timer");
// if the timer exists, clear it
if( !isNaN(timer) ) clearTimeout(timer);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -