jquery ui sortables connect lists: copy items - jquery-ui

I have two lists, I want both of them to be sortable and want to be able to copy (drag) items from list1 to list2 and vice versa.
http://jqueryui.com/demos/sortable/#connect-lists
Is what I want, but the items are moved, not copied.
I did a few experiments with draggables and droppables, but I can't get to to work keeping them sortable. For example: http://jsfiddle.net/tunafish/dvXmf/

OK here is my app; two lists of images, sortable and you can copy over from the connected list.
If an item already exists in the target it's disabled.
Hopefully useful to someone...
JSFiffle here: http://jsfiddle.net/tunafish/VBG5V/
CSS:
.page { width: 410px; padding: 20px; margin: 0 auto; background: darkgray; }
.album { list-style: none; overflow: hidden;
width: 410px; margin: 0; padding: 0; padding-top: 5px;
background: gray;
}
.listing { margin-bottom: 10px; }
.album li { float: left; outline: none;
width: 120px; height: 80px; margin: 0 0 5px 5px; padding: 5px;
background: #222222;
}
li.placeholder { background: lightgray; }
JS:
$("ul, li").disableSelection();
$(".album, .favorites").sortable({
connectWith: ".album, .favorites",
placeholder: "placeholder",
forcePlaceholderSize: true,
revert: 300,
helper: "clone",
stop: uiStop,
receive: uiReceive,
over: uiOver
});
$(".album li").mousedown(mStart);
var iSender, iTarget, iIndex, iId, iSrc, iCopy;
var overCount = 0;
/* everything starts here */
function mStart() {
// remove any remaining .copy classes
$(iSender + " li").removeClass("copy");
// set vars
if ($(this).parent().hasClass("listing")) { iSender = ".listing"; iTarget = ".favorites"; }
else { iSender = ".favorites"; iTarget = ".listing"; }
iIndex = $(this).index();
iId = $(this).attr("id");
iSrc = $(this).find("img").attr("src");
iCopy = $(iTarget + " li img[src*='" + iSrc + "']").length > 0; // boolean, true if there is allready a copy in the target list
// disable target if item is allready in there
if (iCopy) { $(iTarget).css("opacity","0.5").sortable("disable"); }
}
/* when sorting has stopped */
function uiStop(event, ui) {
// enable target
$(iTarget).css("opacity","1.0").sortable("enable");
// reset item vars
iSender = iTarget = iIndex = iId = iSrc = iCopy = undefined;
overCount = 0;
// reinit mousedown, live() did not work to disable
$(".album li").mousedown(mStart);
}
/* rolling over the receiver - over, out, over etc. */
function uiOver(event, ui) {
// only if item in not allready in the target
if (!iCopy) {
// counter for over/out (numbers even/uneven)
overCount++;
// if even [over], clone to original index in sender, show and fadein (sortables hides it)
if (overCount%2) {
if (iIndex == 0) { ui.item.clone().addClass("copy").attr("id", iId).prependTo(iSender).fadeIn("slow"); }
else { ui.item.clone().addClass("copy").attr("id", iId).insertAfter(iSender + " li:eq(" + iIndex + ")").fadeIn("slow"); }
}
// else uneven [out], remove copies
else { $(iSender + " li.copy").remove(); }
}
// else whoooooo
}
/* list transfers, fix ID's here */
function uiReceive(event, ui) {
(iTarget == ".favorites") ? liPrefix = "fli-" : liPrefix = "lli-";
// set ID with index for each matched element
$(iTarget + " li").each(function(index) {
$(this).attr("id", liPrefix + (index + 1)); // id's start from 1
});
}
HTML:
<div class="page">
<div class="container">
<h2>Photo Album</h2>
<ul class="listing album">
<li id="li-1"><img src="tn/001.jpg" /></li>
<li id="li-2"><img src="tn/002.jpg" /></li>
<li id="li-3"><img src="tn/003.jpg" /></li>
<li id="li-4"><img src="tn/004.jpg" /></li>
<li id="li-5"><img src="tn/005.jpg" /></li>
</ul>
</div>
<div style="clear:both;"></div>
<div class="container">
<h2>Favorites</h2>
<ul class="favorites album">
<li id="fli-1"><img src="tn/001.jpg" /></li>
<li id="fli-2"><img src="tn/002.jpg" /></li>
<li id="fli-3"><img src="tn/010.jpg" /></li>
</ul>
</div>
</div>

I have to say that FFish's answer to this has been incredibly helpful to me.
I'd make one suggestion; if the lists are being constantly changed the mousedown event seem to be called numerous times because of the re-registering of the event on all the child objects. It might be a bit of a kludge, but I've added an unbind first to ensure the mousedown event is only called once.
$(".album li").mousedown(mStart);
to
$(".album li").unbind('mousedown', mStart).mousedown(mStart);

Related

jQuery Droppable: Only allow element to drop, if there is no other element in that dropzone

I'm writing a puzzle, where you have to drag an item into the correct dropzone.
Problem: I want that you can only drag an item into a dropzone, if that dropzone does not contain any other items. How can I check, whether there are no other items in that dropzone?
Here is a gif of my current puzzle:
Here is a gif which shows the problem:
As you can see, I can drag multiple items into the same dropzone.
If a dropzone already contains an item, the user should not be able to drop another item into that dropzone. How do I achieve that?
My script so far:
$( ".draggable" ).draggable({ revert: 'invalid', snap: ".dropfield", snapTolerance: 30, snapMode: "inner"});
$( ".dropfield" ).droppable({
accept: ".dropling",
drop: function( event, ui ) {
if(some-condition){ // if correct word got dragged into the correct dropzone
var id = ui.draggable.attr('id');
$("#" + id).draggable( 'disable' );
$(this).droppable( 'disable' );
$("#" + id).css( "background-color", "#7FFF00");
}
});
Html-excerpt:
<div id="liebe" class="dropling draggable text-center">
Liebe
</div>
<span class="dropfield" value="scheitern">
</span>
PS: There are already several topics on Stack-Overflow with the same question. However, I'm not intelligent enough to apply the suggested answers to my case. Please help me.
Edit1
Here is a gif which shows my preferred behavior:
I dragged a wrong word into a dropzone. But as long that dropzone is occupied by a word, no other words should be able to be dropped into that dropzone.
My current code:
if(some-condition){ //correct word
$("#" + id).draggable( 'disable' );
$(this).droppable( 'disable' );
$("#" + id).css( "background-color", "#7FFF00");
}
} else { //wrong word
console.log("wrong word dropped");
$(this).droppable( 'disable' );
}
As soon as I drag the wrong word out of the dropzone, the dropzone should become enabled again. But how can I achieve that?
I would advise breaking this into their own functions. This way you can enable and disable drop repeatedly. Not sure what you want to trigger the item to become draggable and droppable again based on the example you have supplied. Based on what you have supplied, I can offer this the following example.
$(function() {
function enableDrop($target) {
console.log("Enabled Drop");
$target.droppable({
accept: ".dropling",
classes: {
"ui-droppable-hover": "drop-target"
},
drop: function(event, ui) {
var $that = $(this),
dragWord = ui.draggable.text().trim(),
$item = ui.draggable;
if (checkWord(dragWord)) {
console.log("Accepted: " + $item.attr("id"));
$item.
removeClass("draggable")
.draggable('disable')
.attr("style", "")
.appendTo($that);
disableDrop($that);
$that.css("background-color", "#7FFF00");
} else {
return false;
}
}
});
}
function disableDrop($target) {
console.log("Disabling Drop on " + $target.attr("class"));
$target.droppable("destroy");
}
function checkWord(w) {
var result = false;
console.log("Checked Word: " + w);
if (w == "Liebe") {
result = true;
}
return result;
}
$(".draggable").draggable({
revert: 'valid',
snap: ".dropfield",
snapTolerance: 30,
snapMode: "inner"
});
enableDrop($(".dropfield"));
});
p .dropfield {
border: 1px solid #ccc;
border-radius: 3px;
display: inline-block;
width: 4em;
height: 1.5em;
margin-bottom: -.25em
}
p .drop-target {
border: 1px dashed #ccc;
background-color: #ccc;
}
.text-center {
text-align: center;
}
.draggable {
border: 1px solid #ccc;
border-radius: 3px;
display: inline-block;
width: 4em;
height: 1em;
padding: .25em 0;
margin-bottom: -.25em
}
<link rel="stylesheet" href="//code.jquery.com/ui/1.12.1/themes/base/jquery-ui.css">
<script src="https://code.jquery.com/jquery-1.12.4.js"></script>
<script src="https://code.jquery.com/ui/1.12.1/jquery-ui.js"></script>
<p>Diese Schlussfolgerung ist <span class="dropfield" value="scheitern"></span>: Ee kann doch nicht sein, dass es gut ist, </p>
<div id="liebe" class="dropling draggable text-center">Liebe</div>
<div id="absurd" class="dropling draggable text-center">absurd</div>
The easiest way here is probably to solve the whole thing in a more generic way. For this I would add an attribute to the respective Dom element (data-count) and then check how many characters are contained and how many are still allowed:
See /** ADDED **/ for the things i did:
$(function() {
function textWrapper(str, sp, btn) {
if (sp == undefined) {
sp = [0, 0];
}
var txt = "";
if (btn) {
txt = "<span class='w b'>" + str + "</span>";
} else {
txt = "<span class='w'>" + str + "</span>";
}
if (sp[0]) {
txt = " " + txt;
}
if (sp[1]) {
txt = txt + " ";
}
return txt;
}
function chunkWords(p) {
var words = p.split(" ");
words[0] = textWrapper(words[0], [0, 1]);
var i;
for (i = 1; i < words.length; i++) {
var re = /\[.+\]/;
if (re.test(words[i])) {
var b = makeTextBox(words[i].slice(1, -1));
words[i] = " " + b.prop("outerHTML") + " ";
} else {
if (words[0].indexOf(".")) {
words[i] = textWrapper(words[i], [1, 0]);
} else {
words[i] = textWrapper(words[i], [1, 1]);
}
}
}
return words.join("");
}
function unChunkWords(tObj) {
var words = "";
$(tObj).contents().each(function(i, el) {
if ($(el).hasClass("b")) {
words += "[" + $(el).text() + "]";
} else {
words += $(el).text();
}
});
return words.replace(/\s+/g, " ").trim();
}
function makeBtn(tObj) {
var btn = $("<span>", {
class: "ui-icon ui-icon-close"
}).appendTo(tObj);
}
function makeTextBox(txt) {
var sp = $("<span>", {
class: "w b"
}).html(txt);
makeBtn(sp);
return sp;
}
function makeDropText(obj) {
return obj.droppable({
drop: function(e, ui) {
var txt = ui.draggable.text();
var newSpan = textWrapper(txt, [1, 0], 1);
$(this).after(newSpan);
makeBtn($(this).next("span.w"));
makeDropText($(this).next("span.w"));
$("span.w.ui-state-highlight").removeClass("ui-state-highlight");
update()
},
over: function(e, ui) {
$(this).add($(this).next("span.w")).addClass("ui-state-highlight");
},
out: function() {
$(this).add($(this).next("span.w")).removeClass("ui-state-highlight");
}
});
}
$("p.given").html(chunkWords($("p.given").text()));
$("p.given").on("click", ".b > .ui-icon", function() {
$(this).parent().remove();
});
$("p.given").blur(function() {
var w = unChunkWords($(this));
$(this).html(chunkWords(w));
makeDropText($("p.given span.w"));
});
$("span.given").draggable({
helper: "clone",
revert: "invalid"
});
makeDropText($("p.given span.w"));
/** ADDED **/
// update at beginning
update();
// register update events
$("p.given").on('click keydown keyup drag drop', update);
function update(e) {
var templateText = unChunkWords($("p.given"));
var templateTextWithoutParameters = templateText.replace(/\[(.+?)\]/g, "");
var templateTextWithoutParametersLenght = templateTextWithoutParameters.length;
// calc total length
var totalLength = templateTextWithoutParametersLenght;
// since 'helper: clone' we have to ignore it!
$("[data-count]:not(.ui-draggable-dragging)").each(function(index, item) {
var count = $(item).attr("data-count")
var text = "[" + $(item).text() + "]";
var length = templateText.split(text).length - 1;
totalLength += count * length;
});
// 46,8 keycodes for delete & backspace
var maxLength = 200;
if (totalLength >= maxLength && e && e.keyCode !== 46 && e.keyCode !== 8) {
e.preventDefault();
}
// disable data counts
var remaining = maxLength - totalLength;
$("[data-count]:not(.ui-draggable-dragging)").each(function(index, item) {
var count = $(item).attr("data-count");
if (parseInt(count) > remaining) {
$(item).attr("disabled", true);
$(item).draggable().draggable('disable');
} else {
$(item).attr("disabled", false);
$(item).draggable().draggable('enable');
}
})
$(".output").text(totalLength);
}
});
p.given {
display: flex;
flex-wrap: wrap;
}
p.given span.w span.ui-icon {
cursor: pointer;
}
div.blanks {
display: inline-block;
min-width: 50px;
border-bottom: 2px solid #000000;
color: #000000;
}
div.blanks.ui-droppable-active {
min-height: 20px;
}
span.answers>b {
border-bottom: 2px solid #000000;
}
span.given {
margin: 5px;
}
/** ADDED **/
[disabled] {
color: grey
}
<link rel="stylesheet" href="//code.jquery.com/ui/1.12.1/themes/base/jquery-ui.css">
<script src="https://code.jquery.com/jquery-1.12.4.js"></script>
<script src="https://code.jquery.com/ui/1.12.1/jquery-ui.js"></script>
<div class="row">
<p class="given" contenteditable="true">Lorem Ipsum is [Test] Ipsum has been the industry's [America] standard dummy text ever since the 1500s, </p>
</div>
<div class="divider"></div>
<div class="section">
<section>
<div class="card blue-grey ">
<div class="card-content white-text">
<div class="row">
<div class="col s12">
<span class="given btn-flat white-text red lighten-1" rel="1" data-count="50">Test</span>
<span class="given btn-flat white-text red lighten-1" rel="2" data-count="30">America</span>
<span class="given btn-flat white-text red lighten-1" rel="3" data-count="20">Qatar</span>
<span class="given btn-flat white-text red lighten-1" rel="4" data-count="10">Philippines</span>
</div>
</div>
</div>
</div>
</section>
</div>
<div class="divider"></div>
Count: <span class="output"></span>

how to get current slide index: uib carousel

I would like to get the current slide index onChange and not onClick but I don't know how do do that.
First, I had it with ng-click, like this:
In the .html:
<ul uib-carousel id="myCarousel" active="active" no-wrap="noWrapSlides"
style="background-color: #0a0a0a; height:100%; width:100%; padding: 0">
<li id="mySlides" uib-slide ng-repeat="slide in slides track by slide.id" index="slide.id"
style="background-color: #0a0a0a; height: 100%;width: 100%;">
<img ng-src="{{slide.imageUrl}}" ng-click="setSelectedImage(slide.data)" style="background-color: #0a0a0a; height: 100%;width: 100%; max-height: 640px; max-width: 800px"/>
</li>
</ul>
And in the .js:
$scope.myInterval = 5000;
$scope.noWrapSlides = false;
$scope.active = 0;
var currIndex = 0;
var slides = $scope.slides = [];
$scope.addSlide = function(media,data) {
slides.push({
imageUrl: $scope.urlWsImg+media.mediaId+"/stream/800",
id: currIndex++,
data:data
});
};
$scope.addSlide($scope.currentImage.media,$scope.currentImage);//bootstrap
for(var i=0;i<enrichedImages.length;i++) {
var enrichedImage=enrichedImages[i];
$scope.addSlide(enrichedImage.previewImage,enrichedImage);
}
$scope.setSelectedImage =function(imageUrl){
$scope.currentEnrichedImage=imageUrl;
}
I've tried on put an onchange="setSelectedImage(slide.data)" in #my-carousel, but it doesn't work since the slide is unknown when the change occurs.
I hope someone can help me.
You can't use ngChange because the directive doesn't expose any ngModel
From the ngChange docs
Note, this directive requires ngModel to be present.
But you have the active property that you can $watch
$scope.$watch('active', function(newIndex, oldIndex) {
if (Number.isFinite(newIndex) && newIndex!==oldIndex) {
// invoke your function here
}
});
Here is a forked plunk
It seems that active is holding the current index.
You should be able to just use $scope.active.

Revert draggable after a different draggable is dropped

I have a 2x2 grid of droppable areas [[A,B][C,D]] and under the grid is a 1x4 grid of draggables. I only want certain draggables next to each other. So for example, if there is a draggable in B, and I drag a different draggable into A, is there a way to make the draggable in B revert? The draggables have data-row and data-col so that I can grab the draggable in the prev/next column if I need to.
$(".draggable").draggable({
scroll: false,
snap: ".snaptarget",
snapMode: "inner",
stack: ".draggable",
revert: function (event, ui) {
var $draggable = $(this);
$draggable.data("uiDraggable").originalPosition = {
top: 0,
left: 0
};
return !event;
}
});
$(".snaptarget").droppable({
accept: ".draggable",
drop: function (event, ui) {
var $draggable = $(ui.draggable);
var $droppable = $(this);
// This droppable is taken, so don't allow other draggables
$droppable.droppable('option', 'accept', ui.draggable);
ui.draggable.position({
my: "center",
at: "center",
of: $droppable,
using: function (pos) {
$draggable.animate(pos, "fast", "linear");
}
});
// Disable prev or next droppable if the pagewidth == 1
if ($droppable.data("col") == 1) {
$droppable.next().droppable("option", "disabled", true);
var nextDrag = $(".draggable[data-row='" + $droppable.data("row") + "'][data-col='2']");
if (nextDrag.length) {
// I need to revert nextDrag if there is one.
// I've tried this but it doesn't seem to work
nextDrag.data("uiDraggable").originalPosition = {
top: 0,
left: 0
}
}
}
},
tolerance: "pointer"
});
Took a little bit of work, I am never good with offsets and positioning. Here's the key:
function returnItem(item, target) {
// Get Origin
var oPos = item.data("uiDraggable").originalPosition;
// Adjust Postion using animation
item.position({
my: "top left",
at: "top left+" + oPos.left,
of: target,
using: function(pos) {
item.animate(pos, "fast", "linear");
}
});
}
Here is a working example based on the Draggable Snap to element grid example:
https://jsfiddle.net/Twisty/a4ucb6y3/6/
HTML
<div id="target">
<div class="snaptarget ui-widget-header" data-col="1" data-row="1" style="top: 0; left: 0;">
</div>
<div class="snaptarget ui-widget-header" data-col="2" data-row="1" style="top: 0; left: 80px;">
</div>
<div class="snaptarget ui-widget-header" data-col="1" data-row="2" style="top: 80px; left: 0;">
</div>
<div class="snaptarget ui-widget-header" data-col="2" data-row="2" style="top: 80px; left: 80px;">
</div>
</div>
<br style="clear:both">
<div id="source">
<div id="drag-A" class="draggable ui-widget-content" style="left: 0;">
<p>Drag A</p>
</div>
<div id="draggable2" class="draggable ui-widget-content" style="left: 80px;">
<p>Drag B</p>
</div>
<div id="draggable3" class="draggable ui-widget-content" style="left: 160px;">
<p>Drag C</p>
</div>
<div id="draggable4" class="draggable ui-widget-content" style="left: 240px;">
<p>Drag D</p>
</div>
</div>
CSS
.draggable {
width: 80px;
height: 80px;
font-size: .9em;
position: absolute;
top: 0;
}
.draggable p {
text-align: center;
height: 1em;
margin-top: 30px;
}
#source {
width: 320px;
height: 80px;
position: relative;
}
#target {
width: 160px;
height: 160px;
position: relative
}
.snaptarget {
width: 80px;
height: 80px;
position: absolute;
}
jQuery
$(function() {
function returnItem(item, target) {
// Get Origin
var oPos = item.data("uiDraggable").originalPosition;
// Adjust Postion using animation
di.position({
my: "top left",
at: "top left+" + oPos.left,
of: target,
using: function(pos) {
item.animate(pos, "fast", "linear");
}
});
}
$(".draggable").draggable({
scroll: false,
snap: ".snaptarget",
snapMode: "inner",
stack: ".draggable",
revert: "invalid",
start: function(e, ui) {
var off = $("#source").position();
ui.helper.data("uiDraggable").originalPosition = {
top: ui.position.top,
left: ui.position.left
};
}
});
$(".snaptarget").droppable({
accept: ".draggable",
drop: function(event, ui) {
var $draggable = $(ui.draggable);
var $droppable = $(this);
// This droppable is taken, so don't allow other draggables
$droppable.droppable('option', 'accept', ui.draggable);
// Disable prev or next droppable if the pagewidth == 1
if ($droppable.data("col") == 1) {
$droppable.next().droppable("option", "disabled", true);
var nextDrag = $(".draggable[data-row='" + $droppable.data("row") + "'][data-col='2']");
if (nextDrag.length) {
// I need to revert nextDrag if there is one.
returnItem(nextDrag, $("#source"));
}
}
},
tolerance: "pointer"
});
});
In draggable, when we start to drag, we want to record the original position (in case we need to later revert). The revert option is set to invalid in case the user drags it off some other place weird.
We add the position to data of the dragged item so that it can be read later.
When that item is dropped is when the magic happens. You had done all the checking, just needed to return the item if it didn't fit. if nextDrag exists, we return it to it's source.
Going forward, you may want to consider appending, cloning, and removing the elements in the start/stop events. As it is now, we're really only adjust the positioning of the elements, not their hierarchy in the DOM. Depending on what your needs are, this may not matter.

Bootstrap 3 column class interfering with jquery-ui droppable div

I'm using jQuery-UI v1.11.2 in order to create some draggable and droppable divs, and Boostrap 3.1.1 as well.
I'd like to know why a Boostrap column class is interfering with the draggable "hint".
In other words, as I drag an image from my Gallery div to my Dashboard div, the Dashboard div comes in FRONT of the image hint. Then once I DROP my image on the Dashboard div, the image re-appears.
If I remove the col-md-8 class from the Dashboard div, this problem goes away.
Here are two screen shots to demonstrate:
1) WITHOUT the Bootstrap column class on the right div (image hint looks good)
2) With the Bootstrap column class on the right div (image hint disappears)
Here's the HTML code :
<section id="gadgets-view" class="mainbar" data-ng-controller="gadgets">
<div class="container-fluid">
<div class="row-fluid">
<!-- based on http://jqueryui.com/droppable/#photo-manager -->
<div class="ui-widget ui-helper-clearfix col-md-4"> <-- GALLERY OF WIDGETS -->
<ul id="gallery" class="gallery ui-helper-reset ui-helper-clearfix">
<li class="ui-widget-content ui-corner-tr">
<h5 class="ui-widget-header">Tree Grid</h5>
<img src="images/treegrid.jpg" alt="Hierarchy Grid" width="96" height="72">
</li>
<li class="ui-widget-content ui-corner-tr">
<h5 class="ui-widget-header">Area Chart</h5>
<img src="images/chart_area.jpg" alt="Area Chart" width="96" height="72">
</li>
<li class="ui-widget-content ui-corner-tr">
<h5 class="ui-widget-header">Bar Chart</h5>
<img src="images/chart_bar.png" alt="Bar Chart" width="96" height="72">
</li>
<li class="ui-widget-content ui-corner-tr">
<h5 class="ui-widget-header">Column Chart</h5>
<img src="images/chart_column.png" alt="Column Chart" width="96" height="72">
</li>
<li class="ui-widget-content ui-corner-tr">
<h5 class="ui-widget-header">Line Chart</h5>
<img src="images/chart_line.png" alt="Line Chart" width="96" height="72">
</li>
</ul>
</div>
<div class="col-md-8">
<div id="dashboard" class="ui-widget-content ui-state-default "> <-- DROPPABLE DASHBOARD -->
<h4 class=""><span class="ui-icon ui-icon-image"></span>Dashboard</h4>
</div>
<div>
</div>
</div>
</section>
The CSS :
<style>
.ui-widget-header{
font-size: 65%;
font-family: "Trebuchet MS", "Helvetica", "Arial", "Verdana", "sans-serif";
}
.ui-state-highlight {
border: 2px dashed #d3d3d3; /* override to show dashed border when dragging */
}
#gallery {
float: left;
width: 75%;
min-height: 12em;
}
.gallery.custom-state-active {
background: #eee;
}
.gallery li {
list-style: none;
float: left;
width: 96px;
padding: 0.4em;
margin: 0 0.4em 0.4em 0;
text-align: center;
}
.gallery li h5 {
margin: 0 0 0.4em;
cursor: move;
}
.gallery li a {
float: right;
}
.gallery li a.ui-icon-zoomin {
float: left;
}
.gallery li img {
width: 100%;
cursor: move;
}
#dashboard {
float: left;
width: 45%;
height:500px;
padding: 1%;
}
#dashboard h4 {
line-height: 25px;
margin: 0 0 0.4em;
}
#dashboard h4 .ui-icon {
float: left;
}
#dashboard .gallery h5 {
display: none;
}
The JavaScript to create drag/drop areas :
<script>
$(function () {
// there's the gallery and the dashboard
var $gallery = $("#gallery"),
$dashboard = $("#dashboard");
// let the gallery items be draggable
$("li", $gallery).draggable({
cancel: "a.ui-icon", // clicking an icon won't initiate dragging
revert: "invalid", // when not dropped, the item will revert back to its initial position
containment: "document",
helper: "clone",
cursor: "move"
});
// let the dashboard be droppable, accepting the gallery items
$dashboard.droppable({
accept: "#gallery > li",
activeClass: "ui-state-highlight",
drop: function (event, ui) {
debugger;
deleteImage(ui.draggable);
}
});
// let the gallery be droppable as well, accepting items from the dashboard
$gallery.droppable({
accept: "#dashboard li",
activeClass: "custom-state-active",
drop: function (event, ui) {
recycleImage(ui.draggable);
}
});
// image deletion function
var recycle_icon = "<a href='link/to/recycle/script/when/we/have/js/off' title='Remove gadget' class='ui-icon ui-icon-minus'></a>";
function deleteImage($item) {
$item.fadeOut(function () {
var $list = $("ul", $dashboard).length ?
$("ul", $dashboard) :
$("<ul class='gallery ui-helper-reset'/>").appendTo($dashboard);
//$item.find("a.ui-icon-dashboard").remove(); // DO NOT REMOVE ORIGINAL WIDGET ICON - 11/19/2014 BM:
$item.append(recycle_icon).appendTo($list).fadeIn(function () {
//$item.animate({ width: "48px" }).find("img").animate({ height: "36px" });
$item.animate().find("img").animate();
});
});
}
// image recycle function
var dashboard_icon = "<a href='link/to/dashboard/script/when/we/have/js/off' title='Add this gadget' class='ui-icon ui-icon-plus'</a>";
function recycleImage($item) {
$item.fadeOut(function () {
$item
.find("a.ui-icon-refresh")
.remove()
.end()
.css("width", "96px")
.append(dashboard_icon)
.find("img")
.css("height", "72px")
.end()
.appendTo($gallery)
.fadeIn();
});
}
// image preview function, demonstrating the ui.dialog used as a modal window
function viewLargerImage($link) {
var src = $link.attr("href"),
title = $link.siblings("img").attr("alt"),
$modal = $("img[src$='" + src + "']");
if ($modal.length) {
$modal.dialog("open");
} else {
var img = $("<img alt='" + title + "' width='384' height='288' style='display: none; padding: 8px;' />")
.attr("src", src).appendTo("body");
setTimeout(function () {
img.dialog({
title: title,
width: 400,
modal: true
});
}, 1);
}
}
// resolve the icons behavior with event delegation
$("ul.gallery > li").click(function (event) {
var $item = $(this),
$target = $(event.target);
if ($target.is("a.ui-icon-dashboard")) {
deleteImage($item);
} else if ($target.is("a.ui-icon-zoomin")) {
viewLargerImage($target);
} else if ($target.is("a.ui-icon-refresh")) {
recycleImage($item);
}
return false;
});
});
I found the problem you describe (but i did not found a relation with the Bootstrap Grid Classes).
For my the problem seems to be related to the z-index and can be solved by adding the following style rules at the end of your CSS code:
.ui-draggable-handle {
z-index: 1;
}

Highlight Nav item when using anchor scrolling

I'm pretty new when it comes to jQuery so I need someone with a little more experience to help me out. I have a Nav with 3 items - Work About Contact
By default I have work selected but I would like it to change the active class to which ever is clicked. I have anchor scrolling working but I would like to have it highlight to the correct nav item when clicked. Also if it is possible when scrolling down the page and getting to the next section for it to change highlight automatically.
This is the jQuery I am using for the anchor scrolling.
<script>$(function() {
var main-nav = $("#main-nav"), pos = main-nav.offset();
$(window).scroll(function() {
if($(this).scrollTop() > (pos.top + 10) && $(this).scrollTop() < 15000 && main-nav.css('position') == 'static') { main-nav.addClass('fixed'); }
else if($(this).scrollTop() <= pos.top && main-nav.hasClass('fixed')){ main-nav.removeClass('fixed'); }
else if($(this).scrollTop() > 15000 && main-nav.hasClass('fixed')){ main-nav.removeClass('fixed'); }
})
});
</script>
<script>$(function() {
$('ul.nav a').bind('click',function(event){
var $anchor = $(this);
$('html, body').stop().animate({
scrollTop: $($anchor.attr('href')).offset().top
}, 1500,'easeInOutExpo');
/*
if you don't want to use the easing effects:
$('html, body').stop().animate({
scrollTop: $($anchor.attr('href')).offset().top
}, 1000);
*/
event.preventDefault();
});
});
</script>
Here is the CSS
#main-nav{
font: bold 12px 'Bitter', serif;
width: 145px;
float: right;
}
#main-nav li{
float: left;
list-style: none;
margin: 10px 2px 0 2px;
color: #c4c5c5;
}
#main-nav li:last-child{
margin-right: 0;
}
#main-nav a{
text-decoration: none;
color: #c4c5c5;
}
#main-nav a:hover{
text-decoration: none;
color: #919292;
}
#main-nav a.active{
color: #919292;
}
Here is the HTML
<div id="main-nav" class="">
<ul class="nav">
<li><a class="active" href="#work">Work</a></li>
<li>/</li>
<li>About</li>
<li>/</li>
<li>Contact</li>
</ul>
</div>
If someone could help me out I would really really appreciate it!
Since you are using the active class to specify which anchor should be highlighted, you just need to make sure that the correct anchor has that class. Fixing that when the nav items are clicked is straightforward, in your $('ul.nav a').bind add:
$('ul.nav a').removeClass('active');
$(this).addClass('active');
Here is a jsbin.
To highlight the nav's as the page is scrolled you would just compare the scrollTop for the page with the offsets of elements in the DOM...similar to your current code in $(window).scroll.

Resources