Let me explain my situation:
I've 3 positions where images can be hold. They don't have to be filled all.
Empty div | Img 1 | Img 2
When I change the Img 1 to the second place it would be:
Empty div | Img 2 | Img 1
Sortable works fine with this. But I want to save this to the database. The empty div has no ID or data which I want to use. I only want to use the Img's data. So the Img has a data-imgId attribute or something which I want to pass in an array with Javascript.
I want that in my array the "Current position" and the "Data-imgId" are added.
Something like this:
Array
(
[230] = 2
[120] = 1
)
The 230 and 120 are the imageId's and the 2 and 1 are the sortId's. Now I just can update my database with the where clause on the imageId and then set the sortId on the good sort.
With the next code I can check the item which is moved, but the "automatically" moved other image is not returned. How can I fix this:
$( "#pages" ).sortable({
helper: "clone",
update: function(event, ui) {
console.log($(ui.item).attr("id"));
console.log($(ui.item).index() + 1);
}
});
This code returns the id and the position of the dragged object. But I also want to retrieve the other auto moved image id and new position so I can use that for my Database?
Instead use sort(event, ui) which will give you additional information such as the new and original positions of the sorted item. You can then iterate over your array, reassigning position indexes based on these values.
http://api.jqueryui.com/sortable/#event-sort
UPDATE
Apologies, it seems the API no longer includes the before and after indexes. Instead you can use both the start(event, ui) and stop(event, ui) events, and record down the before and after indexes yourself using $(ui.item).index(). After you know these, you can then iterate over your original array, sorted by index position, and update the indexes accordingly.
UPDATE 2
Try this:
$("#pages").sortable({
stop: function(event, ui) {
$("#pages > img").each(function(i, item) {
var id = $(this).attr("id");
array[id] = i;
});
});
});
UPDATE 3
The following revision caters for multiple items, initialised by the classname sort.
var array = { One : {}, Two: {} };
$(".sort").sortable({
stop: function(event, ui) {
var id = $(this).attr("id");
$(this).children().each(function(i, item) {
var itemid = $(item).attr("id");
array[id][itemid] = i;
});
console.log(array)
}
});
See my fiddle.
Related
I'm currently making a small app to practice JavaScript and jQuery.
I have an array of fruits.
var fruits = [apples, bananas, grapes, watermelons, tomatoes];
And on a click event I want to remove tomatoes but if I change my mind clicking the same button, it will add tomatoes back to the array fruits. I've been using splice to remove but I don't know what to use to add the splice element back into the array.
Edit for clarification:
The element is not necessary going to be tomatoes, but it could be any random element in the fruits array. I'm using
fruits.splice(i,1);
To insert a value back into an array (at the same position) after you spliced it, can in general be done like this:
// delete:
var deleted = fruits.splice(i, 1);
// restore:
fruits.splice(i, 0, deleted);
Note that deleted is an array with one element here.
It can also done by taking a kind of backup of the original array:
// backup
var backup = fruits.slice(); // copy
// delete:
fruits.splice(i, 1);
// restore:
fruits = backup;
Undo Stack
To support multiple undo actions, you could use an undo stack, which would just keep track of all the versions of your array. When the user performs an undo-action, the previous version is popped from that stack. This way you can undo more than one removal:
var fruits = ['Apples', 'Bananas', 'Grapes', 'Watermelons', 'Tomatoes'];
var undoStack = [];
function showFruits() {
$('#fruits').html('').append(
// translate the array to a list of LI elements with delete buttons
fruits.map(function(fruit) {
return $('<li>').text(fruit).append(
$('<button>').addClass('delete').text('Delete'));
})
);
}
$(document).on('click', 'button.delete', function () {
undoStack.push(fruits.slice()); // save a copy of the current array on the stack
fruits.splice($(this).parent().index(), 1); // remove from array at index
showFruits(); // update display
});
$('#undo').click(function () {
if (!undoStack.length) return; // cannot undo
fruits = undoStack.pop(); // get previous state
showFruits(); // update display
});
showFruits(); // show initial list
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<ul id="fruits"></ul>
<button id="undo">Undo</button>
More memory efficient alternative
If you are troubled by the memory usage of storing the complete array each time you delete an element, you could use the following alternative functions, which will only store the index and deleted value at every delete action:
$(document).on('click', 'button.delete', function () {
var i = $(this).parent().index(); // get index where to delete
var deleted = fruits.splice(i, 1); // remove from array at that index
undoStack.push([i, deleted]); // save the index and value on the stack
showFruits(); // update display
});
$('#undo').click(function () {
if (!undoStack.length) return; // cannot undo
var restore = undoStack.pop(); // get information for re-inserting
fruits.splice(restore[0], 0, restore[1]); // insert the value
showFruits(); // update display
});
If you would use the undo principle also for other modifications, like undoing an insert, or a modification of the label, then the first solution would not need much modification, while the more memory-efficient one would need a bit more.
For a more generic and elaborated solution on undo/redo operations on any object (not only arrays), see How to version control an object?
If the array position does not matter:
fruits.push(tomatoes);
If you want to insert it at a specific position (index) in the array:
fruits.splice(index, 0, tomatoes);
will insert tomatoes into fruits at the specified index (deleting 0 items first, so it's just an insert).
Array.prototype.splice()
The splice() method changes the content of an array by removing existing elements and/or adding new elements.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/splice
Temp-storing deleted elements and re-adding them
var deletedFruits = fruits.splice(i,1); will contain an array of the removed element(s) because that is the return value of splice(). So
fruits = fruits.concat(deletedFruits);
will re-add the deleted fruits.
Re-adding deleted elements at their original position
Store the position of the deleted element:
var deletedFruit = { fruit: fruits.splice(i,1)[0], index: i }
If need be you can restore deleted fruits at their original array position using the aforementioned
fruits.splice(deletedFruit.index, 0, deletedFruit.fruit);
You can add and remove fruits with these two functions:
function addFruit(fruit) {
fruits.push(fruit);
}
function removeFruit(fruit) {
// indexOf method returns index of fruit in the list, or -1 if fruit is not found.
var index = fruits.indexOf(fruit);
if (index > -1) {
fruits.splice(index, 1);
}
}
This assumes you have already defined an array named fruits. Then you can do something like
<script>
function updateOutput() {
document.getElementById('output').innerHTML = fruits.join(', ');
}
function addSelectedFruit() {
var selectedFruit = document.getElementById('fruit-select').value;
addFruit(selectedFruit);
updateOutput();
}
function removeSelectedFruit() {
var selectedFruit = document.getElementById('fruit-select').value;
removeFruit(selectedFruit);
updateOutput();
}
</script>
<input type="text" id="fruit-select"/>
<button onclick="addSelectedFruit();">Add</button>
<button onclick="removeSelectedFruit();">Remove</button>
List of fruits:
<p id="output"></p>
Example:
<script>
var fruits = ['Apples', 'Pears', 'Pineapples'];
function addFruit(fruit) {
fruits.push(fruit);
}
function removeFruit(fruit) {
var i = fruits.indexOf(fruit);
if (i > -1) {fruits.splice(i, 1);}else{alert(fruit + ' cannot be removed, as it is not present in the array of fruits.');}
}
function selectedFruit() {
return document.getElementById('fruit-select').value;
}
function updateOutput() {
document.getElementById('output').innerHTML = fruits.join(', ');
}
</script>
Fruit:
<input type="text" id="fruit-select" value="Orange"/>
<button onclick="addFruit(selectedFruit());updateOutput();">Add</button>
<button onclick="removeFruit(selectedFruit());updateOutput();">Remove</button>
<p id="output">Apples, Pears, Pineapples</p>
I have an animation linked to scroll position. Whenever the the user scrolls up or down, an animation is triggered for that position to move an element within the view window. If the user scrolls farther, these animations need to queue so that the element moves smoothly along the path.
var target = getAnimation();
var props = {
left: [target.x, target.easing],
top: target.y
};
$("#ball").animate(props, 400, "easeInOutQuad");
The problem with this is that when multiple animations get queued, the ball slows and speeds up in a bad way. What I'd like to do is something like this:
var target = getAnimation();
var props = {
left: [target.x, target.easing],
top: target.y
};
var ball = $("#ball"), queue = ball.queue();
if(ball.queue().length) {
for(var i = 1, len = queue.length; i < len; i++) {
//modify all the other queued animations to use linear easing
}
ball.animate(props, 400, "easeOutQuad");
}
else {
ball.animate(props, 400, "easeInQuad");
}
By starting with an easeIn function, using linear in the middle, and easeOut at the end, I get a much smoother animation. Is there anyway I can access and modify the animations in the queue?
Edit:
Here is a fiddle to demonstrate what I'm trying to achieve: https://jsfiddle.net/reesewill/mtepvguw/
In the fiddle, I am using linear easing, but I'd really like the general affect to be more like easeInOutQuad. However, because I allow queueing, I can't just apply that easing function without it messing up the whole effect (change the linear to easeInOutQuad and click queue a few times quickly to see). Thus, I need something like the above to create the general impression of easeInOutQuad.
Note
, $(selector).queue() returns a reference to the animation queue, an Array. This reference can be modified with standard array methods. See also .dequeue() .
Try utilizing
Array.prototype.splice()
Summary
The splice() method changes the content of an array by
removing existing elements and/or adding new elements.
Syntax
array.splice(start, deleteCount[, item1[, item2[, ...]]])
Parameters
start
Index at which to start changing the array. If greater than the
length of the array, actual starting index will be set to the length
of the array. If negative, will begin that many elements from the end.
deleteCount
An integer indicating the number of old array elements to
remove. If deleteCount is 0, no elements are removed. In this case,
you should specify at least one new element. If deleteCount is greater
than the number of elements left in the array starting at start, then
all of the elements through the end of the array will be deleted.
itemN
The element to add to the array. If you don't specify any
elements, splice() will only remove elements from the array.
Returns
An array containing the deleted elements. If only one element is
removed, an array of one element is returned. If no elements are
removed, an empty array is returned.
See also Array.prototype.concat()
var elem = $("body")
, msg = function() {
return "<br />"
+ "queue length:"
+ $(this).queue("abc").length
};
elem.queue("abc", [
function(next) {
$(this).append(msg.call(this));
next()
},
function(next) {
$(this).append(msg.call(this));
next()
},
function(next) {
$(this).append(msg.call(this));
next()
}
]);
elem.append(msg.call(elem));
// do stuff,
// replace `function` within `abc` queue,
// change `easing` options within replacement function
elem.queue("abc").splice(1, 1, function(next) {
$(this).append("<br />"
+ "`function` at index `1` within `abc` queue "
+ "replaced with new `function`"
+ msg.call(this));
next()
});
elem.append("<br />"
+ "after `.splice()` , before `.concat()`"
+ msg.call(elem));
// do stuff,
// `concat` functions onto `abc` queue`
var arr = elem.queue("abc").concat(
function(next) {
$(this).append(msg.call(this));
next()
}, function(next) {
$(this).append(msg.call(this));
next()
}, function() {
$(this).append(msg.call(this)
+ "<br />"
+ "done");
}
);
elem.queue("abc", arr);
elem.append("<br />"
+ "after `.concat()`"
+ msg.call(elem));
elem.dequeue("abc");
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js">
</script>
i tried, you can do it with create new (re-ordered) queue
download source http://api.jquery.com/queue/
Example: Set a queue array to delete the queue.
and replace start event with my, its worked.
But functions in queue are stored in array of functions. You need to know order of original queue of animations which you want to changed :( Or you can create new optimalized queue.
$( "#start" ).click(function() {
$( "div" )
.show( "slow" )
.animate({ left: "+=50" }, 5000 )
.animate({ top: "+=50" }, 5000 )
.queue(function() {
$( this ).addClass( "newcolor" ).dequeue();
})
.animate({ left: '-=50' }, 1500 )
.queue(function() {
$( this ).removeClass( "newcolor" ).dequeue();
})
.slideUp();
// get current queue
var currQueue = $( "div" ).queue( "fx");
// create new queue and change order or add/remove animations
var newQueue = [];
newQueue.push(currQueue[1]);
newQueue.push(currQueue[3]); // changed
newQueue.push(currQueue[2]); // changed
newQueue.push(currQueue[5]);
// set new queue to element
$("div").queue("fx", newQueue);
console.log($("div").queue("fx"));
});
more info found in jquery documentation
.queue( [queueName ], newQueue )
Description: Manipulate the queue of functions to be executed, once for each matched element.
important is second parameter newQueue
i hope it helps
I know how to save the position of the list elements to a database or localstorage or something similar. But how can I reorder the list with JavaScript from the positions which are saved in my array?
I had a look and StackOverflow and found the following code, but it doesn't work (it just empties my list):
// Get your list items
var items = $('#sortable').find('li');
// The new index order for each item
var order = store.get('sortableIDsOrder');
// Map the existing items to their new positions
var orderedItems = $.map(order, function(value) {
return items.get(value);
});
// Clear the old list items and insert the newly ordered ones
$('#sortable').empty().html(orderedItems);
My array looks like:
[portrait-sms,portrait-pc,portrait-mail,portrait-calendar,portrait-facebook,portrait-twitter,portrait-whatsapp,portrait-skype,portrait-viber,portrait-instagram]
And my HTML looks like:
<li id="portrait-sms"><a href="sms:">...</li>
<li id="portrait-mail"><a href="mailto:">...</li>
<li id="portrait-pc"><a href="#">...</li>
...
The simplest solution I can think of, given only the array (that I assume you've retrieved from somewhere), is:
// assuming this is the array you've recovered from whereever:
var storedArray = ['portrait-sms',
'portrait-pc',
'portrait-mail',
'portrait-calendar',
'portrait-facebook',
'portrait-twitter',
'portrait-whatsapp',
'portrait-skype',
'portrait-viber',
'portrait-instagram'];
function reorder(orderedArray) {
// caching variables:
var el, pre, p;2
// iterating over the elements of the array, using Array.prototype.forEach:
orderedArray.forEach(function (a, b, c) {
// a: the current element in the array,
// b: the index of the current element in the array,
// c: the array itself
if (b > 0) {
// caching the element with the id of the element in the array:
el = document.getElementById(a);
// finding the parentNode of that element:
p = el.parentNode;
// getting the previous element:
pre = document.getElementById(c[b - 1]);
// inserting the element with the id of the current element
// before the nextSibling of the element with the id of the
// previous element in the array:
p.insertBefore(el, pre.nextSibling);
}
});
}
reorder(storedArray);
JS Fiddle demo.
References:
Array.prototype.forEach().
Node.insertBefore().
Node.parentNode.
If you know the elements you have in the database array before hand and they have static values, you can create a new JavaScript array variable by iterating over database array and by forming a new JS array which you use while loading the UI.
On the other hand, if your requirement is to just sort the array during UI loading time instead of showing elements in a fixed order(as retrieved from database), you can use JQuery Table Plugins like DataTable.
I'm trying to remove an item from a mootools sortable list, then serialize and save the new list.
I'd like to use a little bit of eye-candy rather than a straight destroy() on the element. I've built a fiddle here: http://jsfiddle.net/kBAqJ/4/
Note the order1 and order2 vars. This holds the serialized element before and after removing the item. If you use the destroy method to get rid of the element after removing it from the sortable, you get the right value for order2, eg. 4.
If you use nix(true) instead of destroy, you get 5 as the value of order1 and order2, even though the docs say that nix(true) calls destroy after dissolve.
Is this a bug in Mootools, or am I missing something? Is there a different way to add a dissolve effect while still using destroy that will get the right result?
window.addEvent('domready', function(){
var mySort = new Sortables('#example2 UL', {
clone: true,
revert: true,
opacity: 0.7
});
console.log (mySort.elements.length);
var order1 = mySort.serialize(0);
console.dir(order1);
mySort.removeItems($('item1')).destroy(); // this results in the correct value in the order2 var below
//mySort.removeItems($('item1')).nix({duration: 1000}, true); // this results in the wrong value for order2
console.log (mySort.elements.length);
var order2 = mySort.serialize(0);
console.dir(order2);
});
i don't think you'll find any effect or way which will destroy the element and still show it on the page ;) So it is not a moo tools bug
The serialize function is using the children of the list (ie. the <li> blocks) to make the array.
I would say the easiest way would be to get rid of their reference in the serialized array:
window.addEvent('domready', function(){
var mySort = new Sortables('#example2 UL', {
clone: true,
revert: true,
opacity: 0.7
});
console.log (mySort.elements.length);
var order1 = mySort.serialize(0);
console.dir(order1);
//mySort.removeItems($('item1')).destroy(); // this results in the correct value in the order2 var below
mySort.removeItems($('item1')).nix({duration: 1000}, true); // this results in the wrong value for order2
console.log (mySort.elements.length);
var order2 = mySort.serialize(0).erase("item1"); // we have to erase the item because he may still be in the list of children at this timeā¦
console.dir(order2);
});
Cheers
I've looked through the other questions about this and not found a suitable answer.
What I need is very simple: The index of the currently shown item.
Here's my little chunk of config
$('.carousel').jCarouselLite(
{
btnNext: "#right-navigation",
btnPrev: "#left-navigation",
visible: 1,
afterEnd: function(a)
{
// Code that requires the index
}
});
Note: the a is an object
Thanks!
The elements representing the items
that are visible after the animation
ends are passed in as argument.
so you should be able to do something like
afterEnd: function(a){
var index = $(a[0]).index();
}
to get the index of the first element