jQueryUI Sortable duplicating elements after reinitialising draggable - javascript

I am using a combination of JqueryUI Draggable and Sortable to select images from a grid and drag them into a sort-able container where they can be re ordered. the problem is when i load more images i need to reinitialise draggable to include the added items.
https://jsfiddle.net/Lpj8jthk/2/
Initialise Drag
function initDragAndDrop(){
$( ".ui-draggable" ).draggable({
opacity: 1.0,
helper: 'clone',
revert: 'invalid',
connectToSortable: '#drop-area'
});
$("#drop-area").sortable({
axis:"x",
connectWith: '.connectedSortable'
});
}
$( document ).ready(function() {
load_draggable_images(track_page); //load content
initDragAndDrop();
});
load more button handeler
$("#load-more-draggable-images").click(function (e) { //user clicks on button
track_page++; //page number increment everytime user clicks load button
load_draggable_images(track_page); //load content
initDragAndDrop();
});
function load_draggable_images(track_page){
// $('.animation_image').show(); //show loading image
$.post( 'includes/load-images.php', {'page': track_page}, function(data){
if(data.trim().length == 0){
//display text and disable load button if nothing to load
$("#load_more_button").text("No more records!").prop("disabled", true);
}
var parsed = JSON.parse(data);
// $("#images-container").empty();
$.each(parsed, function(k,v){
var name = v['filename'].split(".").shift()
var htmlString = "<div class='tile' data-timestamp='" + v['time']+ "'>" +
"<div>"+
"<img class='ui-draggable' src='" + v['thumbnail'] + "'> "+
"<p>" + name + "</p>"+
"</div> <br> "+
"</div> ";
$("#images-container").append(htmlString);
});
initDragAndDrop();
//scroll page to button element
// $("html, body").animate({scrollTop: $("#load_more_button").offset().top}, 800);
//hide loading image
// $('.animation_image').hide(); //hide loading image once data is received
});
}
https://jsfiddle.net/Lpj8jthk/2/

In your success callback, I would perform:
$("#images-container").sortable("refresh");
Read More: http://api.jqueryui.com/sortable/#method-refresh
Update
From the fiddle, there are a few things I would suggest.
JavaScript
var track_page = 1;
function initDrag($t) {
$t.draggable({
opacity: 1.0,
helper: 'clone',
revert: 'invalid',
connectToSortable: '#drop-area'
});
}
function load_draggable_images(track_page) {
$("#adverts-container").append(makeImage("https://placehold.it/100x100", "ui-draggable"), makeImage("https://placehold.it/100x100", "ui-draggable"), makeImage("https://placehold.it/100x100", "ui-draggable"));
initDrag($("#adverts-container img"));
}
function makeImage(s, c) {
return $("<img>", {
src: s,
class: c
});
}
$(document).ready(function() {
load_draggable_images(track_page);
$("#drop-area").sortable({
axis: "x",
connectWith: '.connectedSortable'
});
initDrag($("#adverts-container img"));
$("#load-more-draggable-images").click(function(e) { //user clicks on button
console.log('Button Clicked');
track_page++; //page number increment everytime user clicks load button
load_draggable_images(track_page); //load content
});
});
There is an order of operations still, so it may be best to define your functions and global variable first. I removed the sortable from your first function, simply because you only have to set it once. The loading button I just made some minor changes to. And I created another function since you will be making a lot of images, why not make that a function.
Once the page has loaded and is ready, we can setup our elements. Load images, set sortable, set draggables, and finally program our click event.
So now you can pass a selector to initDrag() and the element or elements will become draggable and can be dragged into sortable. Sortable only allows x axis, so they can never be removed. You may want to consider a method for removing an image.

Related

jQuery UI Sortable issue when dragged from bottom of page

It works fine from top of page.But once page scrolled, and the section when dragged in sortable list, the section doesn't appear to be at the position of cursor.
$(document).ready(function(){
$('#accordionOne').sortable({
start: function (e, ui) {
// modify ui.placeholder however you like
// ui.placeholder.html("I'm modifying the placeholder element!");
placeholderHeight = ui.item.outerHeight();
ui.placeholder.height(placeholderHeight + 15);
$('<div class="slide-placeholder-animator" data-height="' + placeholderHeight + '"></div>').insertAfter(ui.placeholder);
},
placeholder: "ui-state-highlight",
update: function(event, ui) {
var newOrder = $(this).sortable('toArray').toString();
console.log("data",newOrder);
var posting = $.post('/support_sections/update_order', {order:newOrder, position:'left'});
// Put the results in a div
posting.done(function( data ) {
console.log("data",data);
});
}
});});
Remove overflow: hidden from body.
Good Luck....

jQuery UI Sortable Widget dynamically added lists not triggering events

What I have is have a two sortable list, where one is populated with a set of items. Users should be able to sort between these lists.
They should also be able to create new lists which they can also add the inital items. So the sortable elements are static but the sortable lists are dynamic.
The sortable events are triggered for the initial two lists and work fine. However the problem is with the dynamically added lists. They get added no problem and you can sort items into them. The problem is that the none of the events are triggered such as 'receive' or 'activate', so when I drag an element to one of the new lists I want to get the id of the list, but it never triggers any of those events.
Here is a simple fiddle of it
JS Fiddle
$(".connectedSortable").sortable({
connectWith: '.connectedSortable',
receive: function(event, ui) {
var receivingID = ui.item.parent('ul').attr('id');
console.log('receiving id :' + receivingID);
}}).disableSelection();
This never seems to run on the dynamically added lists
function makeSortable(id) {
console.log(id);
$("#" + id).sortable({
connectWith: ".connectedSortable"
,
activate: function(event, ui) {
console.log("activated list" + id);
}}
).disableSelection(); }
This is what is run when the user adds another list.
Here is an update to your JS Fiddle with the problem resolved. LINK!
The $(".connectedSortable").sortable(...) chunk needs to be run at the end of the $('#add_new_list').click(...) function. The .sortable(...) code adds sortable to all existing items but not future items.
The changes I've made wrap the .sortable(...) in a function called refreshHooks() which is run on page load and again every time "Add New List" is clicked.
$(document).ready(function () {
function makeSortable(id) {
console.log(id);
$("#" + id).sortable({
connectWith: ".connectedSortable",
activate: function (event, ui) {
console.log("activated list" + id);
}}
).disableSelection();
}
var list_counter = 2;
$('#add_new_list').click(function () {
$('#add_new_list').before($(
'<ul id="list' + list_counter + '"' +
' class="connectedSortable"></ul>'
));
var lists = {};
lists.list_id = ['list' + list_counter];
makeSortable(lists.list_id);
list_counter++;
refreshHooks();
});
function refreshHooks() {
$(".connectedSortable").sortable({
connectWith: '.connectedSortable',
receive: function (event, ui) {
var receivingID = ui.item.parent('ul').attr('id');
console.log(receivingID);
},
activate: function (event, ui) {
console.log("activated list");
}
}).disableSelection();
}
refreshHooks();
});

alert if already dropped div is dropped again using jquery

Below is my drag and drop script and it seems to be working like a charm, but what I want to add is if I drop a dragable div on a div that already contains the same div than it should give me an alert that this dragable element already exists or something like that, in order to achieve this I applied .prevAll.each() function to get the id's and compare to the last dropped div. So it compares the id's fine but when I drop the already existing div, it completely jumps the .prevAll.each() condition, it alerts its own id like the others but it does not go in the .prevAll, why is that?
Summing up what I want is:
Edit the approach below to get alert if already existed div is dropped.
Or any other approach would also be appreciated if it gives me the same functionality and doesn't affect any other thing, I already tried the .length approach its not working.
I hope I was able to explain, Any help would be appreciated, Thank you
$(".dragable").draggable({
cancel: "a.ui-icon",
revert: true,
helper: "clone",
cursor: "move",
live: true,
revertDuration: 0
});
$('.droppable').droppable({
accept: ".dragable",
activeClass: "ui-state-highlight",
drop: function(event, ui) {
var $item = $(ui.draggable);
if (!$item.hasClass('clone')) {
$item = $item.clone().addClass('clone');
$item.draggable({
cancel: "a.ui-icon",
revert: true,
cursor: "move",
revertDuration: 0
});
}
$(this).addClass('has-drop').append($item);
var divIdIs = $(ui.draggable).attr( "id" );
//if($(".droppable:has()"))
/*if($("#"+divIdIs).length>1){
alert("Yeah it does exist");
}*/
$("#"+divIdIs).prevAll().each(function() {
var upperDiv = $(this).attr('id');
/*var tes=$("#"+divIdIs).length;
alert(tes);*/
if(divIdIs == upperDiv){
alert("Matched");
}
else{
alert("Not Matched");
}
//if(divIdIs == existingdivId){}
//else{}
});
drop: function(event, ui) {
replace the above line as below and try
if (ids.indexOf("," + ui.draggable[0].id + ",") >= 0)
{
alert("This div already exists");
return;
}
ids += ui.draggable[0].id + ",";
Also make sure you declare the below variable globally,
var ids = ",";

Jquery mobile swipe

I have tabs within a dynamic page. The tabs works great when pressed but I would like to add a swipe function to it so that users can also swipe to next tab.
Here is my attempt of trying to make the swipe function work
function goToMatchDetailPage(matchHome, matchAway){
first_part_id = matchHome.substring(0,2);
sec_part_id = matchAway.substring(0,2);
var id = first_part_id.concat(sec_part_id);
//create the html template
var matchPage = $("<div data-role='page' data-url=dummyUrl><div data-role='header'><h1>"
+ matchHome + "</h1></div><div data-role='content'><div data-role='tabs'>"
+ "<div data-role='navbar'>"
+ "<ul>"
+ "<li><a href='#fragment-1'>" + matchHome + "</a></li>"
+ "<li><a href='#fragment-2'>" + matchAway + "</a></li>"
+ "</ul>"
+ "</div>"
+ "<div id='fragment-1'>"
+ "<p>This is the content of the tab 'One', with the id fragment-1.</p>"
+ "</div>"
+ "<div id='fragment-2'>"
+ "<p>This is the content of the tab 'Two', with the id fragment-2.</p>"
+ "</div></div></div>");
//append the new page to the page contanier
matchPage.appendTo($.mobile.pageContainer);
//go to the newly created page
$.mobile.changePage(matchPage);
Here is the ppart that doesn't work
$(function(){
// Bind the swipeleftHandler callback function to the swipe event on div.box
$( "div" ).on( "swipeleft", swipeleftHandler );
// Callback function references the event target and adds the 'swipeleft' class to it
function swipeleftHandler( event ){
//go to the newly created page
$.mobile.changePage('#fragment-2');
}
});
}
!
Try using event delegation:
Because fragment-1 does not exist at the time you are creating the handler, you assign the handler to the document and delegate it to any child elements called fragment-1 that exist now or will exist in the future.
To make it more generic, you can assign classes to the div and delegate to the class instead of an id...
UPDATE
You can't use changepage to go between tabs, instead use the tabs widget active property(http://api.jqueryui.com/tabs/#option-active):
$(document).on("pagecreate", "#page1", function () {
$("#btngo").on("click", function(){
goToMatchDetailPage('Liverpool', 'Southhampton');
});
$(document).on("swipeleft", "#fragment-1", function(){
$(this).parents("div [data-role=tabs]").tabs( "option", "active", 1 );
} );
$(document).on("swiperight", "#fragment-2", function(){
$(this).parents("div [data-role=tabs]").tabs( "option", "active", 0 );
} );
});
Here is a DEMO
The swipe code is assigned to the document and then delegated to the dynamic div. When you swipe on a tab div, we find its parent that is the tab widget container and then set its active tab option to change tabs.
try this simple code
$(document).on("swipeleft", '#'+event.target.id, function () {
var nextpage = $(this).next('div[data-role="page"]');
if (nextpage.length > 0) {
alert(nextpage.attr('id'));
$.mobile.changePage(nextpage, "slide", false, true);
}
});
$(document).on("swiperight", '#'+event.target.id, function () {
var prevpage = $(this).prev('div[data-role="page"]');
if (prevpage.length > 0) {
$.mobile.changePage(prevpage, { transition: "slide", reverse: true }, true, true);
}
});
I'm easier than the others.. It's not the whole solution, but you can get my point.
Option 1
$(document).on("swipeleft", '#page1', function () {
$('#fragment-2').trigger('click');
});
Option 2
$(document).on("swipeleft", '#page1', function () {
$(this).find("div [data-role=tabs]").tabs( "option", "active", 1 );
});
Not sure about which one is better thought :)

Combining masonry, imagesLoaded with ajax functionality

I'm making a site where all internal links make the current page fade out and the new page fade in. This works great for me now. The problem is that I'm trying to combine it with the great masonry plugin. On the first pageload masonry does work, but I can't seem to figure out how to re-fire masonry on the newly loaded content via ajax. I should add that all the items from the current masonry get deleted, and then replaced by new ones.
The masonry code is like this:
$container = $('#container');
$container.imagesLoaded(function(){
$container.masonry({
itemSelector: '.item',
transitionDuration: 0
});
});
And the ajax load code is like this:
var newHash = "",
$mainContent = $("#ajaxcontainer"),
$ajaxSpinner = $("#loader"),
$el;
$('.internal').each(function() {
$(this).attr("href", "#" + this.pathname);
});
$(document).on('click', '.internal', function() {
window.location.hash = $(this).attr("href");
});
$(window).bind('hashchange', function(){
newHash = window.location.hash.substring(1);
if (newHash) {
$mainContent.fadeOut(500, function() {
$ajaxSpinner.fadeIn();
$mainContent.load(newHash + " #container", function() {
$ajaxSpinner.fadeOut( function() {
$mainContent.fadeIn(1000);
});
$('.internal').each(function() {
$(this).attr("href", "#" + this.pathname);
});
});
});
};
});
$(window).trigger('hashchange');
Does anyone have any input as to how to achieve this? Thank you very much.
I finally managed to get it to work!
I hope other people will find this helpful so I'm posting it here for future reference.
One of the problems I had, seems to be that I hid the container while the data was loading. I hid it with fadeOut and fadeIn which seems to cause problems with masonry. Insted of hiding it per se, I now animate the opacity to 0 and back to 1 once the data is loaded. The script is as follows:
$(window).bind('hashchange', function(){
newHash = window.location.hash.substring(1);
if (newHash) {
$('#ajaxcontainer').fadeTo(500, 0, function() {
$ajaxSpinner.fadeIn();
$mainContent.empty();
$.get(newHash, function(data){
var $data = $(data).find("#container > *");
$container.prepend($data).imagesLoaded(function(){
$container.masonry( 'prepended', $data, true );
});
$ajaxSpinner.fadeOut( function() {
$('#ajaxcontainer').fadeTo(1000, 1);
});
});
});
};
});

Categories

Resources