I found this code, and the fiddle that was linked.
This is close to what I am looking for, however I would like to add a counter because I would like list 2 and list 3 to only accept 3 draggable items.
I intend to add more lists to this code for my purposes and they also need to only accept 3 items.
List 1 needs to be able to accept all of the draggable items.
I think I need to create an array with a counter to keep track of all of this, but I don't know how to do that. I don't really understand or know javascript.
Any help with this would be appreciated.
$(document).ready(function(){
$(".droppable").droppable({
drop: function(event, ui) {
var $list = $(this);
$helper = ui.helper;
$($helper).removeClass("selected");
var $selected = $(".selected");
if($selected.length > 1){
moveSelected($list,$selected);
}else{
moveItem(ui.draggable,$list);
}
}, tolerance: "touch"
});
$(".draggable").draggable({
revert: "invalid",
helper: "clone",
cursor: "move",
drag: function(event,ui){
var $helper = ui.helper;
$($helper).removeClass("selected");
var $selected = $(".selected");
if($selected.length > 1){
$($helper).html($selected.length + " items");
}
}
});
function moveSelected($list,$selected){
$($selected).each(function(){
$(this).fadeOut(function(){
$(this).appendTo($list).removeClass("selected").fadeIn();
});
});
}
function moveItem( $item,$list ) {
$item.fadeOut(function() {
$item.find(".item").remove();
$item.appendTo( $list ).fadeIn();
});
}
$(".item").click(function(){
$(this).toggleClass("selected");
});
});
The HTML...
<div id="list1" class="droppable list"><!-- I want this to accept many. -->
<div class="item draggable">1</div>
<div class="item draggable">2</div>
<div class="item draggable">3</div>
<div class="item draggable">4</div>
</div>
<div id="list2" class="droppable list"><!-- I want this to accept only 3. -->
<div class="item draggable">5</div>
<div class="item draggable">6</div>
</div>
<div id="list3" class="droppable list"><!-- I want this to accept only 3. -->
<div class="item draggable">7</div>
</div>
Added a data-max attribute for each droppable list and inside the drop function you can check if the number of elements inside that list reached the max (limit), and if so - just return false.
Here is the change to your code:
$(document).ready(function(){
$(".droppable").droppable({
drop: function(event, ui) {
var $list = $(this);
$helper = ui.helper;
// Check if we reached the maximum number of children.
if ($(this).children().length == $(this).data('max')) {
return false;
}
$($helper).removeClass("selected");
var $selected = $(".selected");
if($selected.length > 1){
moveSelected($list,$selected);
}else{
moveItem(ui.draggable,$list);
}
}, tolerance: "touch"
});
$(".draggable").draggable({
revert: "invalid",
helper: "clone",
cursor: "move",
drag: function(event,ui){
var $helper = ui.helper;
$($helper).removeClass("selected");
var $selected = $(".selected");
if($selected.length > 1){
$($helper).html($selected.length + " items");
}
}
});
function moveSelected($list,$selected){
$($selected).each(function(){
$(this).fadeOut(function(){
$(this).appendTo($list).removeClass("selected").fadeIn();
});
});
}
function moveItem( $item,$list ) {
$item.fadeOut(function() {
$item.find(".item").remove();
$item.appendTo( $list ).fadeIn();
});
}
$(".item").click(function(){
$(this).toggleClass("selected");
});
});
div.list {
border: 1px solid red;
margin: 5px;
min-height: 20px;
}
div.list div {
background: gray;
margin: 4px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/jqueryui/1.12.1/jquery-ui.min.css" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/jqueryui/1.12.1/jquery-ui.min.js"></script>
<div id="list1" class="droppable list" data-max="-1"><!-- I want this to accept many. -->
<div class="item draggable">1</div>
<div class="item draggable">2</div>
<div class="item draggable">3</div>
<div class="item draggable">4</div>
</div>
<div id="list2" class="droppable list" data-max="3"><!-- I want this to accept only 3. -->
<div class="item draggable">5</div>
<div class="item draggable">6</div>
</div>
<div id="list3" class="droppable list" data-max="3"><!-- I want this to accept only 3. -->
<div class="item draggable">7</div>
</div>
Related
In my project I'm using this drag and drop library called gridstack. You can see their documentation on github here. When you hardcode elements inside the dom and initialize the gridstack, those elements are draggable. But when the elements are created dynamically with a forloop, they are not draggable even if they have the proper draggable classes. How can I make this work?
//initialize grid stack
var grid = GridStack.init({
minRow: 5, // don't collapse when empty
cellHeight: 70,
acceptWidgets: true,// acceptWidgets - accept widgets dragged from other grids or from outside (default: false).
dragIn: '.newWidget', // class that can be dragged from outside
dragInOptions: { revert: 'invalid', scroll: false, appendTo: 'body', helper:'clone' }, // clone or can be your function
removable: '#trash', // drag-out delete class
});
//gridstack on change
grid.on('added removed change', function(e, items) {
let str = '';
items.forEach(function(item) { str += ' (x,y)=' + item.x + ',' + item.y; });
console.log(e.type + ' ' + items.length + ' items:' + str );
});
//dynamic elemenets
const arr = ["Bruh cant drag me!", "Nope me neither", "Me too mouhahaha"];
//loop
for (let i = 0; i < arr.length; i++) {
var div = document.createElement('div');
var el = "<div class='newWidget grid-stack-item ui-draggable ui-resizable ui-resizable-autohide'> <div class='grid-stack-item-content' style='padding: 5px;'> "+arr[i]+"</div></div>";
div.innerHTML = el;
$('.dynamic').append(div);
}
.grid-stack-item-removing {
opacity: 0.8;
filter: blur(5px);
}
#trash {
background: rgba(255, 0, 0, 0.4);
padding: 5px;
text-align: center;
}
.grid-stack-item{
background: whitesmoke;
width: 50%;
border: 1px dashed grey;
}
.grid-stack {
background : #F0FFC0;
}
<!-- jquery -->
<script src="https://code.jquery.com/jquery-3.6.0.js"></script>
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
<script src="https://code.jquery.com/ui/1.12.1/jquery-ui.js"></script>
<!-- gridstack-->
<link rel="stylesheet" href="https://gridstackjs.com/node_modules/gridstack/dist/gridstack-extra.min.css"/>
<script src="https://gridstackjs.com/node_modules/gridstack/dist/gridstack-h5.js"></script>
<!-- body-->
<body>
<div id="trash"><span>drop here to remove</span> </div>
<br>
<div class="dynamic"></div>
<div class="newWidget grid-stack-item ui-draggable ui-resizable ui-resizable-autohide">
<div class="grid-stack-item-content" style="padding: 5px;">
<div>
</div>
<div>
<span>I'm original domster! Drag and drop me!</span>
</div>
</div>
</div>
<br>
<div class="grid-stack"></div>
</body>
This issue was raised here. The grid does not automatically track external changes so the grid needs to be re-initialize for the dragIn option to notice the new dynamic widgets
GridStack.setupDragIn()
Full code
//initialize grid stack
var grid = GridStack.init({
minRow: 5, // don't collapse when empty
cellHeight: 70,
acceptWidgets: true,// acceptWidgets - accept widgets dragged from other grids or from outside (default: false).
dragIn: '.newWidget', // class that can be dragged from outside
dragInOptions: { revert: 'invalid', scroll: false, appendTo: 'body', helper:'clone' }, // clone or can be your function
removable: '#trash', // drag-out delete class
});
//gridstack on change
grid.on('added removed change', function(e, items) {
let str = '';
items.forEach(function(item) { str += ' (x,y)=' + item.x + ',' + item.y; });
console.log(e.type + ' ' + items.length + ' items:' + str );
});
//dynamic elemenets
const arr = ["Bruh cant drag me!", "Nope me neither", "Me too mouhahaha"];
//loop
for (let i = 0; i < arr.length; i++) {
var div = document.createElement('div');
var el = "<div class='newWidget grid-stack-item ui-draggable ui-resizable ui-resizable-autohide'> <div class='grid-stack-item-content' style='padding: 5px;'> "+arr[i]+"</div></div>";
div.innerHTML = el;
$('.dynamic').append(div);
}
GridStack.setupDragIn(
'.newWidget',
);
.grid-stack-item-removing {
opacity: 0.8;
filter: blur(5px);
}
#trash {
background: rgba(255, 0, 0, 0.4);
padding: 5px;
text-align: center;
}
.grid-stack-item{
background: whitesmoke;
width: 50%;
border: 1px dashed grey;
}
.grid-stack {
background : #F0FFC0;
}
<!-- jquery -->
<script src="https://code.jquery.com/jquery-3.6.0.js"></script>
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
<script src="https://code.jquery.com/ui/1.12.1/jquery-ui.js"></script>
<!-- gridstack-->
<link rel="stylesheet" href="https://gridstackjs.com/node_modules/gridstack/dist/gridstack-extra.min.css"/>
<script src="https://gridstackjs.com/node_modules/gridstack/dist/gridstack-h5.js"></script>
<!-- body-->
<body>
<div id="trash"><span>drop here to remove</span> </div>
<br>
<div class="dynamic"></div>
<div class="newWidget grid-stack-item ui-draggable ui-resizable ui-resizable-autohide">
<div class="grid-stack-item-content" style="padding: 5px;">
<div>
</div>
<div>
<span>I'm original domster! Drag and drop me!</span>
</div>
</div>
</div>
<br>
<div class="grid-stack"></div>
</body>
I struggle with the following problem
I have a Page where a user can drag and drop files into another DIV. The user now wants to Drag and Drop multiple files with one drag. The problem however is, I found a codesnippet online (cant find it anymore sadly), customised the code to meet the design requests and now I'm stuck with enabling that multifile Drag and Drop.
I'm a complete beginner if it comes to JqueryUI.
The idea is to add checkboxes in the corners of the files/pictures. All selected items should be moved at once with a Drag and Drop.
Idealy the user should be able to mark the necessary file with the mouse Drag and Drop, but that's just nice to have and not a crucial requirement.
I don't want to ask fora complete solution from someone, but rather for possible sources or examples where i can try and create a personal custom solution from.
CODE:
HTML:
<ul class="gallery col-md-8 borderBoxes" id="gallery">
#foreach (var item in Model.PageList)
{
<li class="imageListItem ui-icon-zoomin" id="#item.ID">
<img class="pages small" src="data:image/jpeg;base64,#item.ImageBaase64" onclick="imageLarger(this)" />
</li>
}
</ul>
<div id="trash" class="col-md-3">
</div>
<div id="documentContainer" class="document-container">
<div class="row justify-content-between">
<button id="addNewDocument" class="col-2 btn btn-primary add-doc-button">new document</button>
<form method="post" class="col-md-2 offset-md-10 forward-form">
</form>
</div>
<ul id="newDocuments"></ul>
</div>
<div style="display: none;">
<div id="docDescription" class='col-md-4 form-group documentDescription'>
<label class='control-label input-label'>Dokumenttyp</label>
<select>
<!--stuff that is not important for this post-->
</select>
</div>
</div>
JavaScript:
function imageLarger(image, liID) {
if ($(image).width() === 700) {
$(image).width(100);
setTimeout(function () {
$(image).removeClass("largePages");
}, 300);
} else {
$(image).width(700);
$(image).addClass("largePages");
}
};
$("#addNewDocument").click(function () {
var uid = Math.floor(Math.random() * 26) + Date.now()
var newDocumentBox = $("<li class='row documentContainer justify-content-between' id='" + uid + "'></li>").prependTo("#newDocuments");
var newDocumentDescription = $("#docDescription").clone(true);
newDocumentDescription.appendTo(newDocumentBox);
var newDroppableBox = $("<div class='documents col-md-7 droppableBox' ></div>").appendTo(newDocumentBox);
var newDeleteButton = $("<div class='close-container deleteButton' onclick='deleteDocument(" + uid + ")'><div class='leftright'></div><div class='rightleft'></div></div>").appendTo(newDroppableBox);
newDroppableBox.droppable({
accept: "#gallery > li",
drop: function (event, ui) {
ui.draggable.appendTo(this).fadeIn(function () {
ui.draggable
.animate()
.find("img")
});
}
});
});
function deleteDocument(elementID) {
var element = document.getElementById(elementID);
var listOfPages = element.getElementsByClassName("imageListItem");
var amounOfPages = listOfPages.length;
if (element.style.display !== "none") {
element.style.display = "none";
for (var i = 0; i < amounOfPages; i++) {
$("#gallery").append(listOfPages[0]); // 0 because removing an object decreases $children.length by 1
}
}
};
$(function () {
var $gallery = $("#gallery"),
$trash = $("#trash"),
$document = $("#document"),
trash_icon = "<a href='link/to/trash/script/when/we/have/js/off' title='Delete this image' class='ui-icon ui-icon-trash'>Delete image</a>",
recycle_icon = "<a href='link/to/recycle/script/when/we/have/js/off' title='Recycle this image' class='ui-icon ui-icon-refresh'>Recycle image</a>";
$("li", $gallery)
.draggable({
cancel: "a.ui-icon",
revert: "invalid",
containment: "document",
helper: "clone",
cursor: "move"
});
$trash
.droppable({
accept: "li",
drop: function (event, ui) {
deleteImage(ui.draggable);
}
});
$gallery
.droppable({
accept: "li",
drop: function (event, ui) {
recycleImage(ui.draggable);
}
});
$document
.droppable({
accept: "li",
drop: function (event, ui) {
ui.draggable.appendTo(this).fadeIn(function () {
$item
.animate()
.find("img")
});
deleteImage(ui.draggable);
}
});
function deleteImage($item) {
$item.fadeOut(function () {
var $list = $("ul", $trash).length ?
$("ul", $trash) :
$("<ul class='gallery ui-helper-reset'/>").appendTo($trash);
$item.find("a.ui-icon-trash").remove();
$item.append(recycle_icon).appendTo($list).fadeIn(function () {
$item.addClass('removedPage');
$item
.animate()
.find("img")
});
});
}
function recycleImage($item) {
$item.fadeOut(function () {
$item.removeClass('removedPage');
$item
.find("a.ui-icon-refresh")
.remove()
.end()
.find("img")
.end()
.appendTo($gallery)
.fadeIn();
});
}
$("#recycleAllDocuments")
.on("click", function () {
if ($('#trash').children().length > 0) {
var listOfDeletedItems = document.getElementById("trash").getElementsByClassName("imageListItem");
for (var i = 0; i < listOfDeletedItems.length; i++) {
var $item = $(listOfDeletedItems[i]);
recycleImage($item);
}
}
});
$("deleteButton")
.on("click", function () {
var $item = $(this);
recycleImages($item);
});
$("ul.gallery > li")
.on("click", function (event) {
var $item = $(this),
$target = $(event.target);
if ($target.is("a.ui-icon-trash")) {
deleteImage($item);
} else if ($target.is("a.ui-icon-zoomin")) {
viewLargerImage($target);
} else if ($target.is("a.ui-icon-refresh")) {
recycleImage($item);
}
return false;
});
});
Consider the following example.
$(function() {
var $gallery = $("#gallery"),
$trash = $("#trash"),
$document = $("#document"),
trash_icon = "<a href='link/to/trash/script/when/we/have/js/off' title='Delete this image' class='ui-icon ui-icon-trash'>Delete image</a>",
recycle_icon = "<a href='link/to/recycle/script/when/we/have/js/off' title='Recycle this image' class='ui-icon ui-icon-refresh'>Recycle image</a>";
$("li", $gallery)
.draggable({
cancel: "a.ui-icon",
revert: "invalid",
containment: "document",
helper: function() {
var help = $("<div>", {
class: "ui-draggable-helper"
});
if ($(".ui-selected").length) {
help.data("items", $(".ui-selected"));
help.addClass("multiple");
} else {
help.data("items", $(this));
}
return help;
},
cursor: "move"
});
$gallery.selectable();
$trash
.droppable({
accept: "li",
drop: function(event, ui) {
deleteImage(ui.helper.data("items"));
}
});
$gallery
.droppable({
accept: "li",
drop: function(event, ui) {
recycleImage(ui.draggable);
}
});
$document
.droppable({
accept: "li",
drop: function(event, ui) {
ui.draggable.appendTo(this).fadeIn(function() {
$item
.animate()
.find("img")
});
deleteImage(ui.draggable);
}
});
function deleteImage($item) {
$item.fadeOut(function() {
var $list = $("ul", $trash).length ?
$("ul", $trash) :
$("<ul class='gallery ui-helper-reset'/>").appendTo($trash);
$item.find("a.ui-icon-trash").remove();
$item.append(recycle_icon).appendTo($list).removeClass("ui-selected").fadeIn(function() {
$item.addClass('removedPage');
$item
.animate()
.find("img")
});
});
}
function recycleImage($item) {
$item.fadeOut(function() {
$item.removeClass('removedPage');
$item
.find("a.ui-icon-refresh")
.remove()
.end()
.find("img")
.end()
.appendTo($gallery)
.fadeIn();
});
}
$("#recycleAllDocuments")
.on("click", function() {
if ($('#trash').children().length > 0) {
var listOfDeletedItems = document.getElementById("trash").getElementsByClassName("imageListItem");
for (var i = 0; i < listOfDeletedItems.length; i++) {
var $item = $(listOfDeletedItems[i]);
recycleImage($item);
}
}
});
$("deleteButton")
.on("click", function() {
var $item = $(this);
recycleImages($item);
});
$("ul.gallery > li")
.on("click", function(event) {
var $item = $(this),
$target = $(event.target);
if ($target.is("a.ui-icon-trash")) {
deleteImage($item);
} else if ($target.is("a.ui-icon-zoomin")) {
viewLargerImage($target);
} else if ($target.is("a.ui-icon-refresh")) {
recycleImage($item);
}
return false;
});
});
.gallery {
list-style: none;
}
.gallery .ui-selecting {
background: #ccc;
border-color: #999;
}
.gallery .ui-selected {
background: #eee;
border-color: #222
}
.imageListItem {
width: 100px;
height: 100px;
border: 1px solid #ccc;
border-radius: 3px;
display: inline-block;
margin: 3px;
}
.ui-draggable-helper {
background: #fff;
width: 50px;
height: 65px;
border: 1px solid #999;
border-radius: 3px;
position: relative;
z-index: 100;
}
.ui-draggable-helper:after {
content: "";
width: 0;
height: 0;
border-bottom: 20px solid #999;
border-right: 20px solid #fff;
position: absolute;
left: 29px;
top: -1px;
}
.ui-draggable-helper.multiple:before {
content: "";
width: 50px;
height: 65px;
border-left: 1px solid #999;
border-bottom: 1px solid #999;
border-radius: 3px;
position: absolute;
top: 5px;
left: -5px;
z-index: 0;
}
#trash {
border: 1px solid #ccc;
width: 200px;
height: 200px;
}
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css" integrity="sha384-Vkoo8x4CGsO3+Hhxv8T/Q5PaXtkKtu6ug5TOeNV6gBiFeWPGFN9MuhOf23Q9Ifjh" crossorigin="anonymous">
<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>
<ul class="gallery col-md-8 borderBoxes" id="gallery">
<li class="imageListItem ui-icon-zoomin" id="item-1">
<img class="pages small" src="data:image/jpeg;base64,#item.ImageBaase64" onclick="imageLarger(this)" />
</li>
<li class="imageListItem ui-icon-zoomin" id="item-2">
<img class="pages small" src="data:image/jpeg;base64,#item.ImageBaase64" onclick="imageLarger(this)" />
</li>
<li class="imageListItem ui-icon-zoomin" id="item-3">
<img class="pages small" src="data:image/jpeg;base64,#item.ImageBaase64" onclick="imageLarger(this)" />
</li>
</ul>
<div id="trash" class="col-md-3">
</div>
<div id="documentContainer" class="document-container">
<div class="row justify-content-between">
<button id="addNewDocument" class="col-2 btn btn-primary add-doc-button">new document</button>
<form method="post" class="col-md-2 offset-md-10 forward-form">
</form>
</div>
<ul id="newDocuments"></ul>
</div>
<div style="display: none;">
<div id="docDescription" class='col-md-4 form-group documentDescription'>
<label class='control-label input-label'>Dokumenttyp</label>
<select>
<!--stuff that is not important for this post-->
</select>
</div>
</div>
With Draggable, you can create your own helper with a Function call.
Allows for a helper element to be used for dragging display. Multiple types supported:
String: If set to "clone", then the element will be cloned and the clone will be dragged.
Function: A function that will return a DOMElement to use while dragging.
So if you want to represent multiple items, you can make a helper looks like this and you can use .data() to carry all the items. When you drop the helper, you can then perform the actions needed on each of the items.
Making use of Selectable, the User can select a number of items in the gallery and then drag them to the Trash, in this example.
When i open the html file on google chrome. It is just a blank page. Nothing is loading. If i take out the .js files it loads the content with the .css applied but never with the .js files. Whether I put the .js files in the or at the end of the it still does not show anything. I am using jquery btw and downloaded the file. all files are on the same folder. also tried both jquery-3.3.1.min.js and jquery-migrate-1.4.1.js if it makes a difference. Hoping someone can help. Thanks!
HTML:
<!DOCTYPE html>
<html>
<head>
<title>Title</title>
<link rel="stylesheet" type="text/css" href="testing.css">
</head>
<body>
<div id="products">
<h1 class="ui-widget-header">Blocks</h1>
<div class="ui-widget-content">
<ul>
<li data-id="1" class="credit"> 10000$ </li>
<li data-id="2" class="debit"> -10000$ </li>
<li data-id="3" class="credit"> 10000$ </li>
<li data-id="4" class="credit"> -10000$ </li>
<li data-id="5" class="credit"> Bank </li>
<li data-id="6" class="debit"> Loan </li>
</ul>
</div>
</div>
<div id="shoppingCart1" class="shoppingCart">
<h1 class="ui-widget-header">Credit Side</h1>
<div class="ui-widget-content">
<ol>
<li class="placeholder">Add your items here</li>
</ol>
</div>
</div>
<div id="shoppingCart2" class="shoppingCart">
<h1 class="ui-widget-header">Debit side</h1>
<div class="ui-widget-content">
<ol>
<li class="placeholder">Add your items here</li>
</ol>
</div>
</div>
<script type="text/javascript" src="jquery-migrate-1.4.1.js"></script>
<script type="text/javascript" src="testing.js"></script>
</body>
</html>
.JS
$("#products li").draggable({
appendTo: "body",
helper: "clone"
});
$("#shoppingCart1 ol").droppable({
activeClass: "ui-state-default",
hoverClass: "ui-state-hover",
accept: ".credit",
drop: function(event, ui) {
var self = $(this);
self.find(".placeholder").remove();
var productid = ui.draggable.attr("data-id");
if (self.find("[data-id=" + productid + "]").length) return;
$("<li></li>", {
"text": ui.draggable.text(),
"data-id": productid
}).appendTo(this);
// To remove item from other shopping cart do this
var cartid = self.closest('.shoppingCart').attr('id');
$(".shoppingCart:not(#" + cartid + ") [data-id=" + productid + "]").remove();
}
}).sortable({
items: "li:not(.placeholder)",
sort: function() {
$(this).removeClass("ui-state-default");
}
});
// Second cart
$("#shoppingCart2 ol").droppable({
activeClass: "ui-state-default",
hoverClass: "ui-state-hover",
accept: ".debit",
drop: function(event, ui) {
var self = $(this);
self.find(".placeholder").remove();
var productid = ui.draggable.attr("data-id");
if (self.find("[data-id=" + productid + "]").length) return;
$("<li></li>", {
"text": ui.draggable.text(),
"data-id": productid
}).appendTo(this);
// To remove item from other shopping chart do this
var cartid = self.closest('.shoppingCart').attr('id');
$(".shoppingCart:not(#" + cartid + ") [data-id=" + productid + "]").remove();
}
}).sortable({
items: "li:not(.placeholder)",
sort: function() {
$(this).removeClass("ui-state-default");
}
});
.CSS
h1 { padding: .2em; margin: 0; }
#products { float:left; width:200px; height: 600px; margin-right: 20px; }
#products ul {list-style: disc; padding: 1em 0 1em 3em;}
.shoppingCart{ width: 200px; margin: 20px; float: left; }
.shoppingCart ol { margin: 0; padding: 1em 0 1em 3em; list-style-type: decimal; }
Use
<script type="text/javascript" src="https://code.jquery.com/jquery-3.3.1.js"></script>
Because jquery-migrate not contains entire jquery code.
Of course you can include script from local.
Issues:
Your HTML files do not have CDN links to jquery and jquery UI. They require in the same order. First, you need jquery CDN and second jquery UI cdn
You're using jquery in a testing.js file but you do not have document.ready function.
Solution:
1. Add cdn links for jquery and jquery UI
2. Wrap your javascript code within document.ready function.
Here is a MDN document
Solution
I am trying to make an editor of a sort were you choose your layout for the given section and then drop another paragraph, button, img etc. on to the content area of that layout.
The thing is that when ever i drop the first div "layout" and i try to drop an item to that div, it chooses the wrong droppable area, because it is located "behind" the, as if the z-index is wrong, but the correct area is visible.
i made i jsfiddle as an example of the code, I'am logging the id of the droppable area and the dropped item in the console:
https://jsfiddle.net/g9aragsp/3/
so i would to add one of the "items" to the "insidedrop" instead of the "editor". Ive only set the id "insidedrop" on the first layout for testing purpose.
My html:
<div class="container-fluid">
<div class="row content">
<div class="col-sm-8 col-sm-offset-2">
<div class="row htmlEditor">
<div class="col-sm-3" style="background-color: gray; min-height: 800px;">
<p style="border-bottom: 5px solid black">Layouts</p>
<ul id="layouts" style="list-style-type: none;">
<li id="layout1" class="PickerButton">layout1</li>
<li id="layout2" class="PickerButton">layout2</li>
<li id="layout3" class="PickerButton">layout3</li>
</ul>
<p style="border-bottom: 5px solid black">Items</p>
<ul id="items" style="list-style-type: none;">
<li id="item1" class="PickerButton">button</li>
<li id="item2" class="PickerButton">img</li>
<li id="item3" class="PickerButton">text</li>
</ul>
</div>
<div id="editor" class="col-sm-9" style="background-color: lightgray; min-height: 800px; padding: 20px;">
</div>
</div>
</div>
</div>
</div>
My Javascript:
$(function () {
var $layouts = $("#layouts"),
$items = $("#items"),
$insideDrop = $("#insideDrop"),
$trash = $("#editor");
$("li", $layouts).draggable({
cancel: "button", // these elements won't initiate dragging
revert: "invalid", // when not dropped, the item will revert back to its initial position
containment: "document",
helper: "clone",
cursor: "move"
});
$("li", $items).draggable({
cancel: "button", // these elements won't initiate dragging
revert: "invalid", // when not dropped, the item will revert back to its initial position
containment: "document",
helper: "clone",
cursor: "move"
});
$trash.droppable({
//accept: "#list1 > li",
drop: function (event, ui) {
console.log(ui.draggable.attr("id"));
console.log($(this).attr("id"));
if (ui.draggable.attr("id") == "layout1")
$("#editor").append("<div class='layout1Outer' style='min-height:400px; margin-bottom: 15px;'><div class='layout1imageplaceholder' style='background-color:#8a2be2;height:150px'></div><div class='layout1content' id='insideDrop' style='background-color:bisque;min-height:250px'></div></div>");
if (ui.draggable.attr("id") == "layout2")
$("#editor").append("<div class='layout2Outer' style='min-height:400px; margin-bottom: 15px;'><div class='layout2imageplaceholder' style='background-color:#8a2be2;float:right;height:400px;width:40%'></div><div class='layout2content' style='background-color:bisque;float:left;min-height:400px;width:60%'></div></div>");
if (ui.draggable.attr("id") == "layout3")
$("#editor").append("<div class='layout3Outer' style='min-height:400px; margin-bottom: 15px;'><div class='layout3imageplaceholder' style='background-color:#8a2be2;float:left;height:400px;width:40%'></div><div class='layout3content' style='background-color:bisque;float:right;min-height:400px;width:60%'></div></div>");
}
});
$insideDrop.droppable({
drop: function (event, ui) {
if (ui.draggable.attr("id") == "item1")
$("#insideDrop").append("<span id='button' style='background-color:#dc143c;padding: 10px; width: 50px'>BUTTON</span>");
if (ui.draggable.attr("id") == "item2")
$("#insideDrop").append("<img src='http://via.placeholder.com/50x50'>");
if (ui.draggable.attr("id") == "item3")
$("#insideDrop").append("<p style='background- color: #284B63; padding: 10px; width: 200px; color: white;'>Insert text here</p>");
console.log(ui.draggable.attr("id"));
}
});
});
I figured out a solution to the problem. instead of targeting a nested "droppable" container. i reused the same function and on hover made javascript return the current hovered object, and then append the item to that specific object as so:
var hoveredObj;
function getid(obj) {
hoveredObj = obj;
}
var $layouts = $("#layouts"),
$items = $("#items"),
$insideDrop = $("#insideDrop"),
$editor = $("#editor");
$("li", $layouts).draggable({
cancel: "button", // these elements won't initiate dragging
revert: "invalid", // when not dropped, the item will revert back to its initial position
containment: "document",
helper: "clone",
cursor: "move"
});
$("li", $items).draggable({
cancel: "button", // these elements won't initiate dragging
revert: "invalid", // when not dropped, the item will revert back to its initial position
containment: "document",
helper: "clone",
cursor: "move"
});
$editor.droppable({
//accept: "#list1 > li",
drop: function (event, ui) {
console.log(ui.draggable.attr("id"));
console.log($(this).attr("id"));
if (ui.draggable.attr("id") == "layout1")
$("#editor").append("<div class='layout1Outer' style='min-height:400px; margin-bottom: 15px;'><div class='layout1imageplaceholder' style='background-color:#8a2be2;height:150px'></div><div class='layout1content' id='insideDrop' onmouseover='getid(this)' style='background-color:bisque;min-height:250px'></div></div>");
if (ui.draggable.attr("id") == "layout2")
$("#editor").append("<div class='layout3Outer' style='min-height:400px; margin-bottom: 15px;'><div class='layout3imageplaceholder' style='background-color:#8a2be2;float:left;height:400px;width:40%'></div><div class='layout3content' style='background-color:bisque;float:right;min-height:400px;width:60%'></div></div>");
if (ui.draggable.attr("id") == "layout3")
$("#editor").append("<div class='layout2Outer' style='min-height:400px; margin-bottom: 15px;'><div class='layout2imageplaceholder' style='background-color:#8a2be2;float:right;height:400px;width:40%'></div><div class='layout2content' style='background-color:bisque;float:left;min-height:400px;width:60%'></div></div>");
if (ui.draggable.attr("id") == "item1")
$(hoveredObj).append("<span id='button' style='background-color:#dc143c;padding: 10px; width: 50px'>BUTTON</span>");
if (ui.draggable.attr("id") == "item2")
$(hoveredObj).append("<img src='http://via.placeholder.com/50x50'>");
if (ui.draggable.attr("id") == "item3")
$(hoveredObj).append("<p style='background- color: #284B63; padding: 10px; width: 200px; color: white;'>Insert text here</p>");
}
});
I've created a menu with javascript. The user needs to click on the hamburger menu icon to open it. Once open, there's an accordion menu with various options. I'm using the jscrollpane jquery plugin for the scrollbar styling, because I want to be able to control the style in MS Edge.
The problem I'm having is that when I click on an accordion item with many list items (Class A or Class B in my menu), as you scroll down the menu always bounces back up to the top. It doesn't just rest at the bottom of the list of items. I believe this is being introduced by the jscrollpane plugin, but I'm not sure. I've spent hours making changes, and just can't fix it. Any help greatly appreciated.
Here is a plunker with all of the code, where you can see how it's functioning:
https://plnkr.co/edit/uPh87hA7r3HlsL2DYBAO
Here's the main index file:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset='utf-8' />
<title></title>
<meta name='viewport' content='initial-scale=1,maximum-scale=1,user-scalable=no' />
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.2.4/jquery.min.js"></script>
<link rel="stylesheet" href="//code.jquery.com/ui/1.12.1/themes/base/jquery-ui.css">
<script src="https://code.jquery.com/ui/1.12.1/jquery-ui.js"></script>
<link href="https://fonts.googleapis.com/css?family=Lato" rel="stylesheet">
<script type="text/javascript" src="js/jquery.mousewheel.js"></script>
<script type="text/javascript" src="js/jquery.jscrollpane.min.js"></script>
<link rel="stylesheet" href="css/jquery.jscrollpane.css">
<script src="js/menu.js"></script>
<link media="all" href="css/style.css" rel="stylesheet">
</head>
<body>
<nav id="menu"></nav>
<div id="datas">
<div id="nav" class="open-close">
<div id="navtitle">MENU</div>
<span>menu</span>
<ul class="slide">
<li id="itemdialog" class="nolink">Select item</li>
<div id="accordion">
<h3>Class A</h3>
<div>
<div class="item aush"><div class="itemname">Aush</div><div class="scientificname">Puffinus lherminieri</div></div>
<div class="item blra"><div class="itemname">Blra</div><div class="scientificname">Laterallus jamaicensis</div></div>
<div class="item blsk"><div class="itemname">Blsk</div><div class="scientificname">Rynchops niger</div></div>
<div class="item bwha"><div class="itemname">Bwha</div><div class="scientificname">Buteo platypterus</div></div>
<div class="item cw"><div class="itemname">Cw</div><div class="scientificname">Setophaga cerulea</div></div>
<div class="item osp"><div class="itemname">Osp</div><div class="scientificname">Pandion haliaetus</div></div>
<div class="item redh"><div class="itemname">Redh</div><div class="scientificname">Aythya americana</div></div>
<div class="item whcr"><div class="itemname">Whcr</div><div class="scientificname">Grus americana</div></div>
<div class="item wt"><div class="itemname">Wt</div><div class="scientificname">Hylocichla mustelina</div></div>
</div>
<h3>Class B</h3>
<div>
<div class="item tarpon"><div class="itemname">Tarpon</div><div class="scientificname">Megalops atlanticus</div></div>
<div class="item bm"><div class="itemname">Blm</div><div class="scientificname">Makaira nigricans</div></div>
<div class="item bft"><div class="itemname">Bft</div><div class="scientificname">Thunnus thynnus</div></div>
<div class="item bus"><div class="itemname">Bus</div><div class="scientificname">Carcharhinus leucas</div></div>
<div class="item df"><div class="itemname">Df</div><div class="scientificname">Coryphaena hippurus</div></div>
<div class="item gagg"><div class="itemname">Gagg</div><div class="scientificname">Mycteroperca microlepis</div></div>
<div class="item gulfm"><div class="itemname">Gulfm</div><div class="scientificname">Brevoortia patronus</div></div>
<div class="item gulfs"><div class="itemname">Gulfsn</div><div class="scientificname">Acipenser oxyrinchus</div></div>
<div class="item mutts"><div class="itemname">Mutts</div><div class="scientificname">Lutjanus analis</div></div>
<div class="item sb"><div class="itemname">Sb</div><div class="scientificname">Morone saxatilis</div></div>
<div class="item ws"><div class="itemname">Ws</div><div class="scientificname">Rhincodon typus</div></div>
</div>
<h3>Class C</h3>
<div>
<div class="item sw"><div class="itemname">Sw</div><div class="scientificname">Physeter macrocephalus</div></div>
<div class="item man"><div class="itemname">Man</div><div class="scientificname">Trichechus manatus</div></div>
</div>
<h3>Class D</h3>
<div>
<div class="item gst"><div class="itemname">Gst</div><div class="scientificname">Chelonia mydas</div></div>
<div class="item kemps"><div class="itemname">Kemps</div><div class="scientificname">Lepidochelys kempii</div></div>
<div class="item lbst"><div class="itemname">Lbst</div><div class="scientificname">Dermochelys coriacea</div></div>
<div class="item lst"><div class="itemname">Lst</div><div class="scientificname">Caretta caretta</div></div>
</div>
</div>
<li id="aboutdialog">A Link</li>
<li id="clearall">Another Link</li>
</ul>
</div>
</div>
<script>
$(function(){
$('#nav > ul.slide').jScrollPane();
});
</script>
</body>
</html>
Here's the menu.js file:
// main menu
jQuery(function(){
$('nav ul').addClass('slide js-slide-hidden');
$('li.dropdown a').attr('data-toggle', 'none');
initOpenClose();
});
// open-close init
function initOpenClose() {
jQuery('.open-close').openClose({
activeClass: 'active',
opener: '.opener',
slider: '.slide',
animSpeed: 400,
effect: 'slide'
});
}
/*
* jQuery Open/Close plugin
*/
;(function($) {
function OpenClose(options) {
this.options = $.extend({
addClassBeforeAnimation: true,
hideOnClickOutside: true,
activeClass:'active',
opener:'.opener',
slider:'.slide',
animSpeed: 400,
effect:'fade',
event:'click'
}, options);
this.init();
}
OpenClose.prototype = {
init: function() {
if(this.options.holder) {
this.findElements();
this.attachEvents();
this.makeCallback('onInit');
}
},
findElements: function() {
this.holder = $(this.options.holder);
this.opener = this.holder.find(this.options.opener);
this.slider = this.holder.find(this.options.slider);
},
attachEvents: function() {
// add handler
var self = this;
this.eventHandler = function(e) {
e.preventDefault();
if (self.slider.hasClass(slideHiddenClass)) {
self.showSlide();
} else {
self.hideSlide();
}
};
self.opener.bind(self.options.event, this.eventHandler);
// hover mode handler
if(self.options.event === 'over') {
self.opener.bind('mouseenter', function() {
self.showSlide();
});
self.holder.bind('mouseleave', function() {
self.hideSlide();
});
}
// outside click handler
self.outsideClickHandler = function(e) {
if(self.options.hideOnClickOutside) {
var target = $(e.target);
if (!target.is(self.holder) && !target.closest(self.holder).length) {
self.hideSlide();
}
}
};
// set initial styles
if (this.holder.hasClass(this.options.activeClass)) {
$(document).bind('click touchstart', self.outsideClickHandler);
} else {
this.slider.addClass(slideHiddenClass);
}
},
showSlide: function() {
$('#pane').removeClass('hide');
$('#pane').addClass('show');
var self = this;
if (self.options.addClassBeforeAnimation) {
self.holder.addClass(self.options.activeClass);
}
self.slider.removeClass(slideHiddenClass);
$(document).bind('click touchstart', self.outsideClickHandler);
self.makeCallback('animStart', true);
toggleEffects[self.options.effect].show({
box: self.slider,
speed: self.options.animSpeed,
complete: function() {
if (!self.options.addClassBeforeAnimation) {
self.holder.addClass(self.options.activeClass);
}
self.makeCallback('animEnd', true);
}
});
},
hideSlide: function() {
$('#pane').removeClass('show');
$('#pane').addClass('hide');
var self = this;
if (self.options.addClassBeforeAnimation) {
self.holder.removeClass(self.options.activeClass);
}
$(document).unbind('click touchstart', self.outsideClickHandler);
self.makeCallback('animStart', false);
toggleEffects[self.options.effect].hide({
box: self.slider,
speed: self.options.animSpeed,
complete: function() {
if (!self.options.addClassBeforeAnimation) {
self.holder.removeClass(self.options.activeClass);
}
self.slider.addClass(slideHiddenClass);
self.makeCallback('animEnd', false);
}
});
},
destroy: function() {
this.slider.removeClass(slideHiddenClass).css({display:''});
this.opener.unbind(this.options.event, this.eventHandler);
this.holder.removeClass(this.options.activeClass).removeData('OpenClose');
$(document).unbind('click touchstart', this.outsideClickHandler);
},
makeCallback: function(name) {
if(typeof this.options[name] === 'function') {
var args = Array.prototype.slice.call(arguments);
args.shift();
this.options[name].apply(this, args);
}
}
};
// add stylesheet for slide on DOMReady
var slideHiddenClass = 'js-slide-hidden';
$(function() {
var tabStyleSheet = $('<style type="text/css">')[0];
var tabStyleRule = '.' + slideHiddenClass;
tabStyleRule += '{position:absolute !important;left:-9999px !important;top:-9999px !important;display:block !important}';
if (tabStyleSheet.styleSheet) {
tabStyleSheet.styleSheet.cssText = tabStyleRule;
} else {
tabStyleSheet.appendChild(document.createTextNode(tabStyleRule));
}
$('head').append(tabStyleSheet);
});
// animation effects
var toggleEffects = {
slide: {
show: function(o) {
o.box.stop(true).hide().slideDown(o.speed, o.complete);
},
hide: function(o) {
o.box.stop(true).slideUp(o.speed, o.complete);
}
},
fade: {
show: function(o) {
o.box.stop(true).hide().fadeIn(o.speed, o.complete);
},
hide: function(o) {
o.box.stop(true).fadeOut(o.speed, o.complete);
}
},
none: {
show: function(o) {
o.box.hide().show(0, o.complete);
},
hide: function(o) {
o.box.hide(0, o.complete);
}
}
};
// jQuery plugin interface
$.fn.openClose = function(opt) {
return this.each(function() {
jQuery(this).data('OpenClose', new OpenClose($.extend(opt, {holder: this})));
});
};
//ACCORDION
$( function() {
// allow more than one group to be open at once
$( "#accordion" ).accordion({
collapsible: true,
active: 'none'
});
// JSCROLLPANE
//init menu height
$('#nav > ul.slide').css('height', '270px');
var width = window.innerWidth,
height = window.innerHeight;
var calc = height - 140 + "px";
if (height >= 740){
$('h3#ui-id-1, h3#ui-id-3, h3#ui-id-5, h3#ui-id-7').click( function(){
if($(this).hasClass('ui-accordion-header-active')){
}else{
$('#nav > ul.slide, .jspContainer, .jspTrack, .jspDrag').css('height', '270px');
}
});
}else{
$('h3#ui-id-1, h3#ui-id-3, h3#ui-id-5, h3#ui-id-7').click( function(){
if($(this).hasClass('ui-accordion-header-active')){
$('#nav > ul.slide, .jspContainer, .jspTrack, .jspDrag').css('height', calc);
$('.jspContainer, .jspTrack, .jspDrag').css('overflow-y', 'scroll');
}else{
$('#nav > ul.slide, .jspContainer, .jspTrack, .jspDrag').css('height', '270px');
}
});
};
});
}(jQuery));
This is the jscrollpane plugin: http://jscrollpane.kelvinluck.com/