Hi Dev,
Today, i we will show you jQuery drag and drop menu example. This article will give you simple example of jQuery drag and drop menu example. you will learn jQuery drag and drop menu example. So let’s follow few step to create example of jQuery drag and drop menu example.
Preview:
Example
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1"> <title>JQuery Drag And Drop Menu Example - Codeplaners</title> <link href="style.css" rel="stylesheet" type="text/css"> </head> <body> <div class="container"> <h1 style="margin:30px auto 30px auto;">JQuery Drag And Drop Menu Example - Codeplaners</h1> <div class="dragmenu" id="dragmenu"> <button class="new-item">+</button> <li class="item-blueprint"> <div class="handle dd3-handle"></div> <div class="content"> <span>[item_name]</span> <button class="item-remove">×</button> <div class="edit-box" style="display: none;"> <input type="text" name="title" placeholder="name"> <input type="url" name="http" placeholder="http://"> <i>✎</i> </div> </div> </li> <ol class="list"> </ol> </div> </div> <script src="http://code.jquery.com/jquery-1.11.3.min.js"></script> <script src="jquery.menu.js"></script> <script> $(document).ready(function() { var updateOutput = function(e) { var list = e.length ? e : $(e.target), output = list.data('output'); if (window.JSON) { output.val(window.JSON.stringify(list.nestable('serialize')));//, null, 2)); } else { output.val('JSON browser support required for this demo.'); } }; $('#dragmenu').domenu({ data: '[{"id":1,"title":"Menu 1","http":""},{"id":2,"title":"Menu 2","http":""},{"id":3,"title":"Menu 3","http":""},{"id":4,"title":"Home","http":"","children":[{"id":5,"title":"Menu 5","http":""},{"id":6,"title":"Menu 6","http":"","children":[{"id":7,"title":"Menu 7","http":""},{"id":8,"title":"Menu 8","http":"","children":[{"id":9,"title":"Example","http":"http://google.com"},{"id":10,"title":"Menu 10","http":""}]}]}]},{"id":11,"title":"Menu 11","http":""}]' }).parseJson(); }); </script> </body> </html>
style.css
html { margin: 0; padding: 0; } body { font-size: 100%; margin: 0; font-family: 'Helvetica Neue', Arial, sans-serif; } h1 { font-size: 1.75em; margin: 0 0 0.6em 0; } .container { width: 980px; margin: 0 auto; } .dragmenu { position: relative; display: block; margin: 0; padding: 0; max-width: 600px; list-style: none; font-size: 13px; line-height: 20px; } .edit-box input { border: none; background: transparent; outline: none; font-size: 13px; color: #444; text-shadow: 0 1px 0 #fff; width: 45%; } .edit-box { position: relative; } .edit-box i { right: 0; overflow: hidden; cursor: pointer; position: absolute; } .item-blueprint { display: none; } .list { display: block; position: relative; margin: 0; padding: 0; list-style: none; } .list .list { padding-left: 30px; } .collapsed .list { display: none; } .item, .empty, .placeholder { text-shadow: 0 1px 0 #fff; display: block; position: relative; margin: 0; padding: 0; min-height: 20px; font-size: 13px; line-height: 20px; } .handle { cursor: move; display: block; height: 30px; margin: 5px 0; padding: 5px 10px; color: #333; text-decoration: none; font-weight: bold; border: 1px solid #AAA; background: #E74C3C; background: -webkit-linear-gradient(top, #E74C3C 0%, #C0392B 100%); background: -moz-linear-gradient(top, #E74C3C 0%, #C0392B 100%); background: linear-gradient(top, #E74C3C 0%, #C0392B 100%); -webkit-border-radius: 3px; border-radius: 3px; box-sizing: border-box; -moz-box-sizing: border-box; } .handle:hover { color: #2ea8e5; background: #fff; } .item > button { display: inline-block; position: relative; cursor: pointer; float: left; width: 24px; height: 20px; margin: 5px 5px 5px 30px; padding: 0; white-space: nowrap; overflow: hidden; border: 0; background: transparent; font-size: 12px; line-height: 1; text-align: center; font-weight: bold; color: f black; } .item .item-remove { position: absolute; right: 7px; height: 19px; padding: 0 5px; top: 6px; overflow: auto; } .dd3-item > button:first-child { margin-left: 30px; } .item > button:before { display: block; position: absolute; width: 100%; text-align: center; text-indent: 0; } .placeholder, .empty { margin: 5px 0; padding: 0; min-height: 30px; background: #f2fbff; border: 1px dashed #b6bcbf; box-sizing: border-box; -moz-box-sizing: border-box; } .empty { border: 1px dashed #bbb; min-height: 100px; background-color: #e5e5e5; background-image: -webkit-linear-gradient(45deg, #fff 25%, transparent 25%, transparent 75%, #fff 75%, #fff), -webkit-linear-gradient(45deg, #fff 25%, transparent 25%, transparent 75%, #fff 75%, #fff); background-image: -moz-linear-gradient(45deg, #fff 25%, transparent 25%, transparent 75%, #fff 75%, #fff), -moz-linear-gradient(45deg, #fff 25%, transparent 25%, transparent 75%, #fff 75%, #fff); background-image: linear-gradient(45deg, #fff 25%, transparent 25%, transparent 75%, #fff 75%, #fff), linear-gradient(45deg, #fff 25%, transparent 25%, transparent 75%, #fff 75%, #fff); background-size: 60px 60px; background-position: 0 0, 30px 30px; } .dragel { height: 60px; position: absolute; pointer-events: none; z-index: 9999; } .dragel > .item .handle { margin-top: 0; } .dragel .handle { -webkit-box-shadow: 2px 4px 6px 0 rgba(0,0,0,.1); box-shadow: 2px 4px 6px 0 rgba(0,0,0,.1); } /** * Nestable Draggable Handles */ .content { display: block; height: 30px; margin: 5px 0; padding: 5px 10px 5px 40px; color: #333; text-decoration: none; font-weight: bold; border: 1px solid #ccc; border: 1px solid #898989; background: #fafafa; background: -webkit-linear-gradient(top, #F4F4F4 10%, #C9C9C9 100%); background: -moz-linear-gradient(top, #F4F4F4 10%, #C9C9C9 100%); background: linear-gradient(top, #F4F4F4 10%, #C9C9C9 100%); -webkit-border-radius: 3px; border-radius: 3px; box-sizing: border-box; -moz-box-sizing: border-box; } .content:hover { color: #2ea8e5; background: #E74C3C; background: -webkit-linear-gradient(top, #E5E5E5 10%, #FFFFFF 100%); background: -moz-linear-gradient(top, #E5E5E5 10%, #FFFFFF 100%); background: linear-gradient(top, #E5E5E5 10%, #FFFFFF 100%); } .dragel > .dd3-item > .content { margin: 0; } .dd3-handle { position: absolute; margin: 0; left: 0; top: 0; cursor: move; width: 30px; text-indent: 100%; white-space: nowrap; overflow: hidden; border: 1px solid #807B7B; text-shadow: 0 1px 0 #807B7B; background: #673ab7; border-top-right-radius: 0; border-bottom-right-radius: 0; } .dd3-handle:before { content: '≡'; display: block; position: absolute; left: 0; top: 3px; width: 100%; text-align: center; text-indent: 0; color: #fff; font-size: 20px; font-weight: normal; } .dd3-handle:hover { background: #E74C3C; background: -webkit-linear-gradient(top, #E74C3C 0%, #C0392B 100%); background: -moz-linear-gradient(top, #E74C3C 0%, #C0392B 100%); background: linear-gradient(top, #E74C3C 0%, #C0392B 100%); }
jquery.menu.js
/*! * Copyright © 2015 Mateusz Zawartka * MIT license */ ;(function($, window, document, undefined) { var hasTouch = 'ontouchstart' in document; /** * Detect CSS pointer-events property * events are normally disabled on the dragging element to avoid conflicts * https://github.com/ausi/Feature-detection-technique-for-pointer-events/blob/master/modernizr-pointerevents.js */ var hasPointerEvents = (function() { var el = document.createElement('div'), docEl = document.documentElement; if (!('pointerEvents' in el.style)) { return false; } el.style.pointerEvents = 'auto'; el.style.pointerEvents = 'x'; docEl.appendChild(el); var supports = window.getComputedStyle && window.getComputedStyle(el, '').pointerEvents === 'auto'; docEl.removeChild(el); return !!supports; })(); var defaults = { listNodeName : 'ol', itemNodeName : 'li', rootClass : 'dragmenu', listClass : 'list', itemClass : 'item', dragClass : 'dragel', handleClass : 'handle', collapsedClass : 'collapsed', placeClass : 'placeholder', noDragClass : 'nodrag', emptyClass : 'empty', expandBtnHTML : '<button data-action="expand" type="button">+</button>', collapseBtnHTML : '<button data-action="collapse" type="button">-</button>', editBtnHTML : '<button data-action="edit" type="button">edit</button>', data : '', group : 0, maxDepth : 5, threshold : 20 }; function Plugin(element, options) { // After $(...).domenu() is called 1: this.w = $(document); this.el = $(element); this.options = $.extend({}, defaults, options); // 2: this.init(); } Plugin.prototype = { init: function() { // Plugin {w: m.fn.init[1], el: m.fn.init[1], options: Object, mouse: Object, isTouch: false…} var list = this; list.reset(); // el = jQuery object // assing to el domenu-group the value user supplied as group property list.el.data('domenu-group', this.options.group); list.placeEl = $('<div class="' + list.options.placeClass + '"/>'); // forEach itemNodeName=li element $.each(this.el.find(list.options.itemNodeName), function(k, el) { // pass the li element // If an element is a parent then it contains another li elements // and can be collapsed and expanded list.setParent($(el)); }); list.el.on('click', 'button', function(e) { // Don't do anything when dragging if (list.dragEl) { return; } var target = $(e.currentTarget), action = target.data('action'), item = target.parent(list.options.itemNodeName); // Some internal click handlers communicating through // jQuery object data if (action === 'collapse') { list.collapseItem(item); } if (action === 'expand') { list.expandItem(item); } }); // Declaring some custom event handlers var onStartEvent = function(e) { var handle = $(e.target); // Identify if the object is draggable if (!handle.hasClass(list.options.handleClass)) { if (handle.closest('.' + list.options.noDragClass).length) { return; } handle = handle.closest('.' + list.options.handleClass); } // If the element is not draggable, or is while dragging // then don't do anything if (!handle.length || list.dragEl) return; // Same here making sure if the object can be draggeds list.isTouch = /^touch/.test(e.type); if (list.isTouch && e.touches.length !== 1) { return; } // Don't do whatever browsers do by default e.preventDefault(); // At this point object is identified as available to drag // so start dragging list.dragStart(e.touches ? e.touches[0] : e); }, onMoveEvent = function(e) { if (list.dragEl) { e.preventDefault(); list.dragMove(e.touches ? e.touches[0] : e); } }, onEndEvent = function(e) { if (list.dragEl) { e.preventDefault(); list.dragStop(e.touches ? e.touches[0] : e); } }; // If thouch events are avialable, start listening for those events if (hasTouch) { list.el[0].addEventListener('touchstart', onStartEvent, false); window.addEventListener('touchmove', onMoveEvent, false); window.addEventListener('touchend', onEndEvent, false); window.addEventListener('touchcancel', onEndEvent, false); } // Start listening for the events below list.el.on('mousedown', onStartEvent); // list.w = $(document) list.w.on('mousemove', onMoveEvent); list.w.on('mouseup', onEndEvent); this.startEditListener($('.dd3-content span')); this.endEditListener($('.dd3-content .edit-box input')); this.addNewListItemListener(this.el.find('.new-item')); }, addNewListItemListener: function(el, parent) { var _this = this, opt = this.options; el.on('click', function(e) { $('.' + opt.rootClass).find('.' + opt.listClass).first().prepend(_this.createNewListItem()); }) }, startEditListener: function(el) { el.on('click', function(e) { var e = $(this); e.add(e.parent().find('.item-remove').first()).slideToggle(50, function() { if(this.nodeName !== 'SPAN') return; var edb = e.parent().children('.edit-box').first(); if(e.text() !== 'no title') edb.children('input[name="title"]').first().val(e.text()); edb.slideToggle(50, function() { edb.children().first('input').focus(); }); }); }); }, endEditListener: function(el) { var opt = this.options; el.on('keypress', function(e) { if(e.keyCode === 13) { var spn = $(e.target).parents('.dd3-content').children('span').first(), removeBtn = $(this).parent().find('.item-remove'); removeBtn.slideToggle(); spn.text($(this).parents().first().children('input[name="title"]').val()); if(spn.text() === '') spn.text('no title'); $(e.target).parents('.dd3-content').children('.edit-box').first().slideToggle(50, function() { var rmvBtn = $(e.target).parents('.dd3-content').find('.item-remove').first(), edtBox = $(e.target).parents('.edit-box').first(), title = edtBox.find('input[name="title"]').first().val(), http = edtBox.find('input[name="http"]').first().val(), item = edtBox.parents(opt.itemNodeName).first(); item.data('title', title).data('http', http); spn.add(rmvBtn).slideToggle(50); }); } }) }, createNewListItem: function(id, title, http) { var id = typeof id !== 'undefined' ? id : this.getHighestId() + 1, title = typeof title !== 'undefined' ? title : 'Item ' + id, http = typeof http !== 'undefined' ? http : '', el = this.el, opt = this.options, blueprint = el.find('.item-blueprint').first().clone(); blueprint.data('id', id); blueprint.attr('class', opt.itemClass); blueprint.find('span').first().text(title); blueprint.find('input[name="title"]').first().val(title); blueprint.data('title', title); blueprint.find('input[name="http"]').first().val(http); blueprint.data('http', http); blueprint.find('.item-remove').first().on('click', function(e) { blueprint.remove(); }) this.startEditListener(blueprint.find('span').first()); this.endEditListener(blueprint.find('.edit-box input')); this.endEditListener(blueprint.find('.edit-box i').first()); return blueprint; }, getHighestId: function() { var opt = this.options, el = this.el, id = 0; el.find(opt.itemNodeName).each(function(i, e) { var eId = $(e).data('id'); if(eId > id) return id = eId; }); return id; }, serialize: function() { var data, depth = 0, list = this; step = function(level, depth) { var array = [ ], items = level.children(list.options.itemNodeName); items.each(function() { var li = $(this), item = $.extend({}, li.data()), sub = li.children(list.options.listNodeName); if (sub.length) { item.children = step(sub, depth + 1); } array.push(item); }); return array; }; data = step(list.el.find(list.options.listNodeName).first(), depth); return data; }, deserialize: function(data, override) { var data = JSON.parse(data) || JSON.parse(this.options.data), _this = this, list = _this.el.find('.list').first(); if(override) list.children().remove(); var processItem = function(i, pref) { if(i.children) { var cref = $('<ol class="list"></ol>'), item = _this.createNewListItem(i.id, i.title, i.http); pref.append(item); item.append(cref); _this.setParent(item, true); jQuery.each(i.children, function(i, e) { processItem(e, cref); }) } else { var item = _this.createNewListItem(i.id, i.title, i.http); pref.append(item); } } jQuery.each(data, function(i, e) { processItem(e, list); }) }, serialise: function() { return this.serialize(); }, reset: function() { this.mouse = { offsetX : 0, offsetY : 0, startX : 0, startY : 0, lastX : 0, lastY : 0, nowX : 0, nowY : 0, distX : 0, distY : 0, dirAx : 0, dirX : 0, dirY : 0, lastDirX : 0, lastDirY : 0, distAxX : 0, distAxY : 0 }; this.isTouch = false; this.moving = false; this.dragEl = null; this.dragRootEl = null; this.dragDepth = 0; this.hasNewRoot = false; this.pointEl = null; }, expandItem: function(li) { li.removeClass(this.options.collapsedClass); li.children('[data-action="expand"]').hide(); li.children('[data-action="collapse"]').show(); li.children(this.options.listNodeName).show(); }, collapseItem: function(li) { var lists = li.children(this.options.listNodeName); if (lists.length) { li.addClass(this.options.collapsedClass); li.children('[data-action="collapse"]').hide(); li.children('[data-action="expand"]').show(); li.children(this.options.listNodeName).hide(); } }, expandAll: function(cb) { var list = this; list.el.find(list.options.itemNodeName).each(function() { var item = $(this); if(cb && cb(item)) list.expandItem($(this)); else list.expandItem($(this)); }); }, collapseAll: function(cb) { var list = this; list.el.find(list.options.itemNodeName).each(function() { var item = $(this); if(cb && cb(item)) list.collapseItem(item); else list.expandItem(item); }); }, setParent: function(li, force) { // If the specified selector targets any element if (li.children(this.options.listNodeName).length || force) { // LI STRUCTURE // <li class="item dd3-item" data-id="15"> // <button data-action="collapse" type="button">Collapse</button> // <button data-action="expand" type="button" style="display: none;">Expand</button> // <div class="handle dd3-handle">Drag</div><div class="dd3-content">Item 15</div> // <ol class="list"> // <li class="item dd3-item" data-id="16"> // <div class="handle dd3-handle">Drag</div><div class="dd3-content">Item 16</div> // </li> // <li class="item dd3-item" data-id="17"> // <div class="handle dd3-handle">Drag</div><div class="dd3-content">Item 17</div> // </li> // <li class="item dd3-item" data-id="18"> // <div class="handle dd3-handle">Drag</div><div class="dd3-content">Item 18</div> // </li> // </ol> // </li> li.prepend($(this.options.expandBtnHTML)); li.prepend($(this.options.collapseBtnHTML)); // make sure handle is the first element var handle = li.find('.' + this.options.handleClass).first().clone(); li.find('.' + this.options.handleClass).first().remove(); li.prepend(handle); } // If the selector gets targeted within the li element // hide it li.children('[data-action="expand"]').hide(); }, unsetParent: function(li) { li.removeClass(this.options.collapsedClass); li.children('[data-action]').remove(); li.children(this.options.listNodeName).remove(); }, dragStart: function(e) { var mouse = this.mouse, target = $(e.target), dragItem = target.closest(this.options.itemNodeName); this.placeEl.css('height', dragItem.height()); mouse.offsetX = e.offsetX !== undefined ? e.offsetX : e.pageX - target.offset().left; mouse.offsetY = e.offsetY !== undefined ? e.offsetY : e.pageY - target.offset().top; mouse.startX = mouse.lastX = e.pageX; mouse.startY = mouse.lastY = e.pageY; this.dragRootEl = this.el; // Define the state as dragging so no other elements get attached due // to the identification process in init onStartDrag this.dragEl = $(document.createElement(this.options.listNodeName)) .addClass(this.options.listClass + ' ' + this.options.dragClass); this.dragEl.css('width', dragItem.width()); // this.placeEl -> $('<div class="' + list.options.placeClass + '"/>'); // Put the targeted element into the dragEl which will work as a kind of a bag // while dragging dragItem.after(this.placeEl); dragItem[0].parentNode.removeChild(dragItem[0]); dragItem.appendTo(this.dragEl); $(document.body).append(this.dragEl); // Adjust the dragging bag (dragEl) initial position within the // element this.dragEl.css({ 'left' : e.pageX - mouse.offsetX, 'top' : e.pageY - mouse.offsetY }); // what is the deepest element within dragEl? var i, depth, items = this.dragEl.find(this.options.itemNodeName); for (i = 0; i < items.length; i++) { depth = $(items[i]).parents(this.options.listNodeName).length; if (depth > this.dragDepth) { this.dragDepth = depth; } } }, dragStop: function(e) { var el = this.dragEl.children(this.options.itemNodeName).first(); el[0].parentNode.removeChild(el[0]); this.placeEl.replaceWith(el); this.dragEl.remove(); this.el.trigger('change'); if (this.hasNewRoot) { this.dragRootEl.trigger('change'); } this.reset(); }, dragMove: function(e) { var list, parent, prev, next, depth, opt = this.options, mouse = this.mouse; // Placeholder will be dragged around, the member list will actually // hide itself and replace the placeholder on dragStop this.dragEl.css({ // Place element on the document following the mouse // position change // // e.pageX position of the mouse relative to the whole page // e.offsetX position of the mouse relative to .dd3-handle // mouse absolute position - the position offset in the element = // = begin offset of the element 'left' : e.pageX - mouse.offsetX, 'top' : e.pageY - mouse.offsetY }); // mouse position last events mouse.lastX = mouse.nowX; mouse.lastY = mouse.nowY; // mouse position this events mouse.nowX = e.pageX; mouse.nowY = e.pageY; // distance mouse moved between events mouse.distX = mouse.nowX - mouse.lastX; mouse.distY = mouse.nowY - mouse.lastY; // direction mouse was moving mouse.lastDirX = mouse.dirX; mouse.lastDirY = mouse.dirY; // direction mouse is now moving (on both axis) mouse.dirX = mouse.distX === 0 ? 0 : mouse.distX > 0 ? 1 : -1; mouse.dirY = mouse.distY === 0 ? 0 : mouse.distY > 0 ? 1 : -1; // axis mouse is now moving on var newAx = Math.abs(mouse.distX) > Math.abs(mouse.distY) ? 1 : 0; // do nothing on first move if (!mouse.moving) { mouse.dirAx = newAx; mouse.moving = true; return; } // calc distance moved on this axis (and direction) // if the direction has changed if (mouse.dirAx !== newAx) { mouse.distAxX = 0; mouse.distAxY = 0; } else { mouse.distAxX += Math.abs(mouse.distX); if (mouse.dirX !== 0 && mouse.dirX !== mouse.lastDirX) { mouse.distAxX = 0; } mouse.distAxY += Math.abs(mouse.distY); if (mouse.dirY !== 0 && mouse.dirY !== mouse.lastDirY) { mouse.distAxY = 0; } } mouse.dirAx = newAx; /** * move horizontal only to right */ if (mouse.dirAx && mouse.distAxX >= opt.threshold) { // reset move distance on x-axis for new phase mouse.distAxX = 0; // this.placeEl placeholder element prev = this.placeEl.prev(opt.itemNodeName); // increase horizontal level if previous sibling exists and is not collapsed if (mouse.distX > 0 && prev.length && ! prev.hasClass(opt.collapsedClass)) { // cannot increase level when item above is collapsed list = prev.find(opt.listNodeName).last(); // check if depth limit has reached depth = this.placeEl.parents(opt.listNodeName).length; if (depth + this.dragDepth <= opt.maxDepth) { // create new sub-level if one doesn't exist if (!list.length) { list = $('<' + opt.listNodeName + '/>').addClass(opt.listClass); list.append(this.placeEl); prev.append(list); this.setParent(prev); } else { // else append to next level up list = prev.children(opt.listNodeName).last(); list.append(this.placeEl); } } } // decrease horizontal level if (mouse.distX < 0) { // we can't decrease a level if an item preceeds the current one next = this.placeEl.next(opt.itemNodeName); if (next.length) { this.placeEl.before(next); } if (!next.length) { parent = this.placeEl.parent(); this.placeEl.closest(opt.itemNodeName).after(this.placeEl); if (!parent.children().length) { this.unsetParent(parent.parent()); } } } } var isEmpty = false; // find list item under cursor if (!hasPointerEvents) { this.dragEl[0].style.visibility = 'hidden'; } this.pointEl = $(document.elementFromPoint(e.pageX - document.body.scrollLeft, e.pageY - (window.pageYOffset || document.documentElement.scrollTop))); if (!hasPointerEvents) { this.dragEl[0].style.visibility = 'visible'; } if (this.pointEl.hasClass(opt.handleClass)) { this.pointEl = this.pointEl.parent(opt.itemNodeName); } if (this.pointEl.hasClass(opt.emptyClass)) { isEmpty = true; } else if (!this.pointEl.length || !this.pointEl.hasClass(opt.itemClass)) { return; } // find parent list of item under cursor var pointElRoot = this.pointEl.closest('.' + opt.rootClass), isNewRoot = this.dragRootEl.data('domenu-id') !== pointElRoot.data('domenu-id'); /** * move vertical */ if (!mouse.dirAx || isNewRoot || isEmpty) { // check if groups match if dragging over new root if (isNewRoot && opt.group !== pointElRoot.data('domenu-group')) { return; } // check depth limit depth = this.dragDepth - 1 + this.pointEl.parents(opt.listNodeName).length; if (depth > opt.maxDepth) { return; } var before = e.pageY < (this.pointEl.offset().top + this.pointEl.height() / 2); parent = this.placeEl.parent(); // if empty create new list to replace empty placeholder if (isEmpty) { list = $(document.createElement(opt.listNodeName)).addClass(opt.listClass); list.append(this.placeEl); this.pointEl.replaceWith(list); } else if (before) { this.pointEl.before(this.placeEl); } else { this.pointEl.after(this.placeEl); } if (!parent.children().length) { this.unsetParent(parent.parent()); } if (!this.dragRootEl.find(opt.itemNodeName).length) { this.dragRootEl.append('<div class="' + opt.emptyClass + '"/>'); } // parent root list has changed if (isNewRoot) { this.dragRootEl = pointElRoot; this.hasNewRoot = this.el[0] !== this.dragRootEl[0]; } } } }; /** * Works like a proxy to Plugin prototype. * Separates the API of the developer from the API * of the user, so we can change whatever we'd like * in the future. * @param {Object, Plugin} plugin * @param {????} lists [????] (don't have time to check, can't recall) */ function PublicPlugin(plugin, lists) { if( ! plugin) throw new TypeError('expected object, got ' + typeof plugin); this._plugin = plugin, this._lists = lists; } PublicPlugin.prototype = { getLists: function (params) { return this._lists; }, parseJson: function(data, override) { var data = data || null, override = override || false; this._plugin.deserialize(data, override); return this; }, toJson: function() { var data = this._plugin.serialize(); return JSON.stringify(data); }, expandAll: function() { this._plugin.expandAll(); return this; }, collapseAll: function () { this._plugin.collapseAll(); return this; }, expand: function(cb) { this._plugin.expandAll(cb); return this; }, collapse: function(cb) { this._plugin.collapseAll(cb); return this; }, getListNodes: function(cb) { var opt = this._plugin.options, listNodes = this._plugin.el.find(opt.listNodeName); return listNodes; } } // $('#domenu').domenu(); $.fn.domenu = function(params) { // jQuery DOM Element // <div class="dd" id="domenu"> // <ol class="list"></ol> // </div> // I.e. returns the element on which domenu() was called on // $('#example').domenu() jQuery will assign $('#example') to this // within this function lists = this.first(); var retval = null, plugin, pPlugin; lists.each(function() { // lists = jQuery object // each sets this, to the current element in iteration if ( ! $(this).data("domenu")) { // Binds new Plugin to $('#domenu').data('domenu')... with specified params $(this).data("domenu", new Plugin(this, params)); $(this).data("domenu-id", new Date().getTime()); plugin = $(this).data("domenu"); pPlugin = new PublicPlugin(plugin, lists); } else { if(typeof params === 'string') { if (typeof pPlugin[params] === 'function') { // proxy retval = pPlugin[params](); } else if (typeof plugin[params] === 'function') { retval = plugin[params](); } } } }); plugin = $(this).data("domenu"); pPlugin = new PublicPlugin(plugin, lists); return retval || pPlugin; }; })(window.jQuery || window.Zepto, window, document);