How to build a Drag and Drop Menu Builder? - javascript

Hi all i present i am wokring on Menu Builder,where user can come build his menu how ever he likes with the subemnus to,i have a problem in Drag and Drop, i had done till drag but drop is not working in my case
i have to achevie some thing like this
http://www.prodevtips.com/demos/drag_drop_tree/
but not similar i should be able to create a chain or tree with drag and drop,my scenario is i have all menus listed at the bottom and and add column button at the top,when user wants to build menu he can click on addmenu button and a column opens there he can drag and drop the menus from the listed menu, from here i want the working scenario of the above link i had given where if i drop on any menu item it should be the child of that parent menu item which the user dropped on
here is my script
$(document).ready(function() {
var i = 0;
$("button[id='columnadd']").click(function () {
alert(1);
var domElement = $('<aside id="shoppingCart' + i + '" class="shoppingCart"><h2 class="ui-widget-header">Add Menu Items Here</h2><aside class="ui-widget-content"><ol><li class="placeholder">Add your items here</li></ol></aside></aside>');
i++;
$(this).after(domElement);
});
$(".small_box li" ).draggable({
appendTo: "body",
helper: "clone"
});
});
$(".small_box li a").droppable({
tolerance : "pointer",
hoverClass : "tree_hover",
drop : function(event, ui){
var dropped = ui.draggable;
dropped.css({top: 0, left: 0});
var me = $(this).parent();
if(me == dropped)
return;
var subbranch = $(me).children("ul");
if(subbranch.size() == 0) {
me.find("a").after("<ul></ul>");
subbranch = me.find("ul");
}
var oldParent = dropped.parent();
subbranch.eq(0).append(dropped);
var oldBranches = $("li", oldParent);
if (oldBranches.size() == 0) { $(oldParent).remove(); }
}
});
and here is my html
<body>
<button id="columnadd" >Add Column</button>
<aside class="menu-structer" id="AddColumns" >
</aside>
<aside class="small_box">
<h4>BRANDS</h4>
<ul>
<li id ="brand1"><a class="" href="#">Brand1</a></li>
<li id ="brand2">Brand2</li>
<li id ="brand3">Brand3</li>
<li id ="brand4">Brand4</li>
</ul>
</aside>
<aside class="small_box">
<h4>CATEGORIES</h4>
<ul>
<li id ="category1">Category1</li>
<li id="category2">Category2</li>
<li id="category3">Category3</li>
<li id="category4">Category4</li>
</ul>
</aside>
<aside class="small_box">
<h4>PRODUCTS</h4>
<ul>
<li id="Product1">Product1</li>
<li id="product2">Product2</li>
<li id="product3">Product3</li>
<li id="product4">Product4</li>
</ul>
</aside>
</body>
can any one help me over here please...that would be of a a great help for me..i am struggling with the Drop Event

Mistakes i had done
I had created a DOM Element (which is the drop area) upon click and this element is not in your page until you click the trigger. By doing $('#shoppingCart ol').droppable({...}) in $(document).ready(function() {}) block you try to add droppable to an element that even not in your page, so this won't do a thing.
changes
What i had done is make the element droppable after create that element
$("button[id='columnadd']").click(function () {
// create the element
var domElement = $('<aside id="shoppingCart' + i++ + '" class="shoppingCart"><h2 class="ui-widget-header">Add Menu Items Here</h2><aside class="ui-widget-content" id="droppable"><ol><li class="placeholder">Add your items here</li></ol></aside></aside>');
// put it after $(this)
$(this).after(domElement);
// at this point you have domElement in your page
// make it droppable
domElement.find("ol").droppable({
// put options here
greedy: true,
drop: function (event, ui) {
makeItDroppableToo(event, ui, this);
}
});
});
and this is the function to make your dropped element has its own placeholder
function makeItDroppableToo(e, ui, obj) {
$(obj).find(".placeholder").remove();
var placeholder = $("<ol><li class='placeholder'>You can also drop it here</li></ol>");
$("<li></li>").append(ui.draggable.text()).append(placeholder).appendTo($(obj));
// this is the same as the previous one
placeholder.droppable({
// put options here
greedy: true,
drop: function (event, ui) {
makeItDroppableToo(event, ui, this);
}
});
}

Related

How to find previously dropped value from droppable div

I am working on dynamic formula generator using jquery drag and drop functionlaity
What i have done so far is
I have two 2 list
<ul id="head">
<li class="horizontal">Salary</li>
<li class="horizontal">Workers</li>
<li class="horizontal">Perday</li>
</ul>
and
<ul id="operations">
<li class="horizontal">Add +</li>
<li class="horizontal">Sub -</li>
<li class="horizontal">Multiply *</li>
<li class="horizontal">Divide /</li>
</ul>
The process is user will first drag from first list and drop in droppable div and they will add anything from operations list like (+,- etc..).
The Jquery i have used :
$('#head li').draggable({
cursor: 'move',
helper: 'clone',
connectToSortable: "#drop",
});
$("#drop").droppable({
drop: function (event, ui) {
$(this)
.removeClass("ui-droppable ui-sortable")
.addClass("horizontal ui-draggable ui-draggable-handle")
}
}).sortable();
droppable div
<div id="drop" style="border:2px solid;min-height:100px;"></div>
My Problem is that i have to validate user should not drop more than one operations at a time. ex:Multiply * Add + this is wrong. The operations must come between 2 text. so how i can we find the previously added value in this droppable div (drop).
Thanks in advance
How about using some data attribiutes to id your operations and a variable in js to store what was dropped last, like this:
HTML
<div class='js-droppable' data-drop-type='op'>operation 1</div>
JS
var lastDropped = '';
var processDroppable = function () {
var dropped = $(this).data('drop-type');
if (lastDropped == 'op' && dropped == 'op') {
console.log('not allowed')
} else {
console.log('you may drop ...');
}
lastDropped = dropped;
}
$('.js-droppable').click(processDroppable);
The data attribute stores the type of droppable and then is used to catch any duplicate droppable that is not allowed.
You could do any logic really based on identifying your droppables, but lastDropped == 'op' && dropped == 'op' covers the case in the question.
The working example is here in JSFiddle, I've used click events rather than dropped events, for simplicity; open the console to see the output.

AngularJS Sortable, can't get the Reciever List (ul) ID

I am relatively new to AngularJS and there is some issues that I am facing. Sorry if my question is stupid.
I have UL lists populated from Angular and I CAN get the ID of the Dragged item,the Index of that Item, the ID of the List it is been dragged From, but NOT the ID of the List it is been Dragged TO. It keeps returning me undefined or null all the time.
Believe me I've tried lots of code variations. Any ideas would be highly appreciated
INDEX.html
<ul id="sortable{$ review[0].progressFlag $}" ui-sortable="sortableOptions" ng-model="review" class="connectedSortable apps-container">
<li id="item{$ item.id $}" ng-repeat="item in review">
<a href="#" class="list-group-item">
<p><b>{$ item.name $}</b></p>
</a>
</li>
</ul>
APP.js
$scope.sortableOptions = {
items:'li',
start: function(event, ui) {
item = ui.item;
console.log(item.attr('id')); // Dragged Item ID - Works
console.log(ui.item.index()); // Dragged Item Index - Works
console.log(ui.item.parent().attr('id')); // The List ID Item being dragged From - Works
},
stop: function(event, ui) {
item = ui.item;
console.log(ui.item);
console.log(ui.item.parent().attr('id')); // The List ID Item being dragged TO = UNDEFINED
},
connectWith: ".apps-container",
};

slideDown jQuery Navigation divs

I'm trying to get something like the navigation on the following site to work.link
I have the following code so far on JSFIDDLE and have it kind of working. The only issue is when hovering over the submenu li's for a second time (or cycling through them) the new background div does not drop down again.
Any suggestions much appreciated - thanks
EDIT:
I've simplified the demo here jsfiddle.net/XLZGP/11 to just the main menu items. I'm looking for the new colour to slide in while the old colour does NOT slideup.
code as follows
HTML
<header>
<div class="inner-header">
<nav>
<ul>
<li class="parent"> bus
<div class="locations-wrapper">
<ul class="sub-menu">
<li>station
</li>
<li>stop
</li>
</ul>
</div>
</li>
<li class="parent"> train
<div class="locations-wrapper">
<ul class="sub-menu">
<li>station
</li>
<li>stop
</li>
</ul>
</div>
</li>
</ul>
</nav>
</div>
<div class="boxes">
<div class="bus"></div>
<div class="bus-station"></div>
<div class="bus-stop"></div>
<div class="train"></div>
<div class="train-station"></div>
<div class="train-stop"></div>
</div>
</header>
JS
var parentList = $('.parent');
var currentTitle;
var currentChildTitle;
parentList.on('mouseover', function () {
$(this).find('.locations-wrapper').slideDown(400);
currentTitle = $(this).find('a').attr('title');
$('.' + currentTitle).addClass('open').slideDown(400);
});
parentList.on('mouseleave', function () {
$(this).find('.locations-wrapper').slideUp(400);
$('.boxes').children('div').removeClass('open').slideUp(400);
});
$('.sub-menu').on('mouseover', 'li', function () {
currentChildTitle = $(this).find('a').attr('title');
currentChildTitle = currentTitle + '-' + currentChildTitle;
$('.boxes').children('div').removeClass('open');
$('.' + currentChildTitle).addClass('open').slideDown(400);
});`
I think what you are looking for is something closer to this, http://jsfiddle.net/un5ke/1/
var mouseLeft = true;
$('.sub-menu').on('mouseleave', 'li', function () {
mouseLeft = true;
setTimeout(function() {
if (mouseLeft) {
var toClose = $('.boxes').children('div.open');
toClose.slideUp();
toClose.removeClass('open');
}
}, 500);
});
$('.sub-menu').on('mouseover', 'li', function () {
mouseLeft = false;
currentChildTitle = $(this).find('a').attr('title');
currentChildTitle = currentTitle + '-' + currentChildTitle;
var toOpen = $('.' + currentChildTitle);
if (!toOpen.hasClass('open')){
var toClose = $('.boxes').children('div.open');
toOpen.addClass('open').slideDown(400, function completed () {
$(toClose).slideUp();
$(toClose).removeClass('open');
});
}
});
There is a couple of things that I have done:
Removed z-index from the open class
Added a half-second delay to a mouse leave, so that if the person is mousing from one to another the background doesn't go crazy.
Removed the mouseleave of the entire menu becuase that isn't the issue at the moment.
Double-checking before opening that the current menu isn't already opened.
You don't have a mouseleave handler for your sub menu items, so once they're down, they're down.
I added a quick mouseleave for the sub-menu's too in a fiddle - should be a starter for the exact effect you want to achieve:
http://jsfiddle.net/XLZGP/
$('.sub-menu').on('mouseleave', 'li', function () {
currentChildTitle = $(this).find('a').attr('title');
currentChildTitle = currentTitle + '-' + currentChildTitle;
$('.' + currentChildTitle).removeClass('open').slideUp(400);
});
Took a different approach and decided to create the slide down div backgrounds dynamically through JS - creating each dom node and then assigning it to the dom, limiting the total number produced.
Overlaying each div with the next. WORKING JSFIDDLE
var parentList = $('.parent');
// declare variables
var currentChildTitle;
var currentTitle;
var banner;
// setup append div function
function dropBanner(currentTitle) {
// create the banner variable dom node
banner = $('<div class="' + currentTitle + '"/></div>');
// add it to the dom
$('.boxes').append(banner);
// animate it
$('.' + currentTitle).slideDown(300);
}
// setup a function to limit the number of divs appended
function chop() {
if ($('.boxes div').length > 15) {
$('.boxes div').eq(0).remove();
}
}
// listen for mouseover the parent list items
parentList.on('mouseover', function (e) {
if (!($(this).find('.locations-wrapper').is(':visible'))) {
$(this).find('.locations-wrapper').slideDown(300);
}
// grab the current list item title
currentTitle = $(this).find('a').attr('title');
// call dropBanner passing the current list item title
dropBanner(currentTitle);
chop();
});
// listen for mouseleave
parentList.on('mouseleave', function () {
$(this).find('.locations-wrapper').delay(300).slideUp(300);
$('.boxes div').delay(300).slideUp(300);
});
// listen for mouseover the submenu list items
$('.sub-menu').on('mouseover', 'li', function (e) {
// grab the current list item title
currentTitle = $(this).find('a').attr('title');
// call dropBanner passing the current list item title
dropBanner(currentTitle);
chop();
// stop the bubbling up effect to parent list items
e.stopPropagation();
});

Unable to Drop on an Empty List

I am making a simple web app. At one part I have to drag among two different lists - One of which may be empty. I am not able to drop to an empty list.
I have tried:
dropOnEmpty = true
dropOnEmpty = "true"
dropOnEmpty = false
dropOnEmpty = "false"
and tried stuff as on:
How do I move an item to an empty list using jquery?
JQuery API
Rollys Wordpress
jQuery UI drop on empty container/list not working
Hidden <li> elements so that lists are not empty at all.
but nothing worked.
This is the HTML part:
<ul><li>Present</li></ul>
<ul id="present" class="sortable"></ul> //First List
<ul><li>Future</li></ul>
<ul id="future" class="sortable"><li></li></ul> //Second List
This is the jQuery function:
$("ul.sortable").sortable({
axis: 'y',
cancel: "div.cal",
connectWith: "ul",
dropOnEmpty: true,
update: function(event, ui) {
if (ui.sender === null) {
var goalsNames = '';
$(".sortable li").each(function() {
if (goalsNames === '') {
goalsNames = '["' + $(this).text();
} else {
goalsNames += '","' + $(this).text();
}
});
goalsNames += '"]';
localStorage.setItem("goalsNames", goalsNames);
var goalsDetails = localStorage.getItem("goalsDetails");
console.log(goalsDetails);
var goalsDetailsObj = JSON.parse(goalsDetails);
$("#present li").each(function() {
console.log(JSON.stringify(goalsDetailsObj[$(this).text()]));
console.log(goalsDetailsObj[$(this).text()].active);
goalsDetailsObj[$(this).text()].active = "present";
});
$("#future li").each(function() {
goalsDetailsObj[$(this).text()].active = "future";
});
localStorage.setItem("goalsDetails", JSON.stringify(goalsDetailsObj));
}
}
}).disableSelection();
});
As far as I know, sortable is a JQuery-UI feature to sort items within one list via drag and drop.
From the docs
Enable a group of DOM elements to be sortable. Click on and drag an element to a new spot within the list
I would try to go with draggable and droppable and you need to initialize a drop target via droppable.
<ul><li>Present</li></ul>
<ul id="present" class="firstList"></ul> <!--First List-->
<ul><li>Future</li></ul>
<ul id="future" class="secondList"><li></li></ul> <!--Second List-->
$('ul.firstList').draggable(/***/);
$('ul.secondList').droppable(/***/);
And read about the many options to be inserted at /***/.
Update
I created a plunker for you as a start point: http://plnkr.co/RPlTgK
Here is the proper example for drag and drop among empty list hope this will help Demo

How to display the new order of the parent after sorting?

Here are my current codes. These codes sort both parent and child but it display only the new order of the child after sorting.
template
<div id="tree">
<ul id="envelopes" class="dropcat cat-data">
<li id="1">
<span class="cat-title">Utilities</span>
<ul class="dropenv mt">
<li class="innerList">Electricity</li>
<li class="innerList">Water</li>
<li class="innerList">Trash</li>
<li class="innerList">Television</li>
</ul>
</li>
<li id="2">
<span class="cat-title">Sample</span>
<ul class="dropenv mt">
<li class="innerList">Test</li>
</ul>
</li>
<li id="3">
<span class="cat-title">Saving</span>
<ul class="dropenv mt">
<li class="innerList">College Fund</li>
<li class="innerList">Retirement Fund</li>
<li class="innerList">Emergency Fund</li>
</ul>
</li>
</ul>
</div>
<p id="cl"></p>
js
<script>
var addPositions = function() {
$('.dropenv, .dropcat').each(function() {
var position = 0;
$(this).children().each(function() {
$(this).data('position', position);
position++;
});
});
};
$(document).ready(function() {
addPositions();
var origTitle;
var origColor;
$(".dropenv").sortable({
connectWith: "ul.mt",
dropOnEmpty: true,
start: function(event, ui) {
origColor = ui.item.text();
origTitle = ui.item.parent().siblings('.cat-title').text();
},
stop: function(event, ui) {
var order = [];
ui.item.closest('ul').children('li').each(function() {
order.push($(this).data('position'));
var c = $(this).text();
if (c === origColor) {
var z = origTitle;
} else {
var z = $(this).parent().siblings('.cat-title').text();
}
$("#cl").append(z + "_" + c + "<br /\>");
});
}
});
$( "ul.dropcat").sortable({
connectWith: "ul.cat-data",
dropOnEmpty: true,
});
});
</script>
How to get the new order of the parent?
For example:
Before:
- Utilities
- Sample
- Saving
//If I'm going to swap Saving to Utilities, it must be display the new order like this
New Order:
- Saving
- Sample
- Utilities
Update from #Jack Shedd
I copy the stop and I get this output:
_ Utilities Electricity Water Natural Gas Home Phone Cell Phones Trash Television Internet
_ Charity/Giving Charitable Gifts Fast Offerings
_ Sample test
_ Recreation Entertainment Vacation
_ Saving College Fund Emergency Fund Retirement Fund
How to get parent only, no child?
Based on the code and the question:
You have a custom stop event handler on your sortable object which, after the user stops dragging, will append the new position of the child to the innerHTML of the #cl paragraph tag.
You're wondering why this works for the children, but not the parent.
If you look at your jQuery, you'll see this line:
$(".dropenv").sortable({
This is applying the sortable behavior to any element with a class of .dropenv. It's here that you're initing the custom stop event handler which updates the #cl tag.
However, you're only applying this behavior to elements with a class of .dropenv, which, reviewing your html for the parent:
<ul id="envelopes" class="dropcat cat-data">
The parent UL doesn't match.
Instead, you're applying an entirely new sortable behavior to the parent:
$( "ul.dropcat").sortable({
connectWith: "ul.cat-data",
dropOnEmpty: true,
});
Which has none of the custom stop handler code contained within it.
So either copy the stop handler code from the .dropenv sortable options into the one above, or make the .dropenv sortable apply to your .dropcat UL as well.

Categories