set order to jquery sortable - javascript

I use jquery draggble function for unorderd list (ul) and face a problem of getting the order of items after it changed and setting the order ofter page is loaded.Suppose we have the following code :
<html>
<head>
<script src="http://code.jquery.com/jquery-latest.js"></script>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.4/jquery.min.js> </script>
<script src="http://ajax.googleapis.com/ajax/libs/jqueryui/1.8/jquery-ui.min.js"></script>
<script>
$(document).ready(function(){
$('.secondblock').sortable({axis:'y'});
$(".block").sortable({
axis: 'y',
update: function(event, ui) {// order changed
var order = jQuery(this).sortable('toArray');
// set this order to .secondblock
}
});
});
</script>
</head>
<body>
<ul class="block" type="none">
<li id = "one">1</li>
<li id = "two">2</li>
<li id = "three">3</li>
<li id = "four">4</li>
<li id = "five">5</li>
</ul>
<ul class="secondblock" type="none">
<li id = "one">1</li>
<li id = "two">2</li>
<li id = "three">3</li>
<li id = "four">4</li>
<li id = "five">5</li>
</ul>
</body>
Are there any possible solutions?

First, you shouldn't have the same id appear twice in a document. That will cause all kinds of problems.
Instead, set a data-attribute on the items in the second list to reflect corresponding items in the first list:
<ul class="block" type="none">
<li id = "one">1</li>
<li id = "two">2</li>
<li id = "three">3</li>
<li id = "four">4</li>
<li id = "five">5</li>
</ul>
<ul class="secondblock" type="none">
<li data-block-id = "one">1</li>
<li data-block-id = "two">2</li>
<li data-block-id = "three">3</li>
<li data-block-id = "four">4</li>
<li data-block-id = "five">5</li>
</ul>
Then reflecting the sort of the first list in the second list is simple:
$('.block').sortable({update: sortOtherList});
function sortOtherList(){
$('.block li').each(function(){
$('.secondblock [data-block-id=' + $(this).attr('id') + ']')
.remove()
.appendTo($('.secondblock'));
});
}
See it working here: http://jsfiddle.net/6HKZG/2/

Faust's looks better to me. Mark it if it's what you wanted.
Yes. I feel like I need more information, but I had to do almost the exact same thing recently.
First, you want to extend the javascript array object with a sorting algorithm. Here is what I use:
Array.prototype.move = function (old_index, new_index) {
if (new_index >= this.length) {
var k = new_index - this.length;
while ((k--) + 1) {
this.push(undefined);
}
}
this.splice(new_index, 0, this.splice(old_index, 1)[0]);
return this; // for testing purposes
};
source Move an array element from one array position to another
Then, what I would do is use this along with an OldPosition/NewPosition function to get the index of the element before and after the sort, and use this method to move the object in the array.
I think JQuery sort let's you get information about the array before and after the sort
ex:
$('li').presort(function() {
window.oldindex = $(this).index();
});
$('li').postsort(function() {
window.newindex = $(this).index();
array.move(oldindex,newindex);
});
If you're just looking to make .secondblock match .block after sort, you can do this:
source: http://jqueryui.com/demos/sortable/#events
$( ".selector" ).sortable({
start: function(event, ui) { //Get the order of the .block list items before dragging }
});
$( ".selector" ).sortable({
stop: function(event, ui) { //Get the new order of the list items after dragging. Compare the two orders and sort .secondblock to match block }
});

Related

Get index of an element from a NodeList

I have a simple list:
<ul id="list">
<li id="item-1">1</li>
<li id="item-2" style="display: none">2</li>
<li id="item-3">3</li>
<li id="item-4">4</li>
<li id="item-5">5</li>
</ul>
And need to get index of a specific item disregarding hidden items.
var list = document.getElementById('list');
var items = list.querySelectorAll('li:not([style*="display: none"])');
I try to convert NodeList in Array:
var list_items = Array.from(items);
But don't known how to run something like that: list_items.indexOf('item-3')
https://codepen.io/marcelo-villela-gusm-o/pen/RwNEVVB?editors=1010
You can make a function to find the id you need in a list you want, passing two parameters, that way you can use this function dynamically.
Based on id, inside the function just need to use .findIndex() that returns the index or -1 if not found.
See here:
var list = document.getElementById('list');
var items = list.querySelectorAll('li:not([style*="display: none"])');
var list_items = Array.from(items);
function getIndexById(idToSearch, list){
//ES6 arrow function syntax
return list.findIndex(elem => elem.id == idToSearch)
//normal syntax
//return list.findIndex(function(elem) {return elem.id == idToSearch})
}
console.log("found at index: ", getIndexById("item-3", list_items))
<ul id="list">
<li id="item-1">1</li>
<li id="item-2" style="display: none">2</li>
<li id="item-3">3</li>
<li id="item-4">4</li>
<li id="item-5">5</li>
</ul>
Not exactly related to the question, but if possible, I would suggest you to change your HTML to remove that inline style of display: none and change it to a class, (e.g: class='hidden'), it would be better for your .querySelector when using :not, for example: li:not(.hidden), since any space in your inline style can break your selector. ("display:none" != "display: none", spot the space)
Maybe like this:
var item = list_items.find(function(item) {
return item.id === "item-3";
});
I would recommend using :not(.hidden) instead of "grepping" for a match on the style tag. Then, simply find the index after casting the NodeList to an array.
For the Vue.js inclined, see this fiddle: https://jsfiddle.net/634ojdq0/
let items = [...document.querySelectorAll('#list li:not(.hidden)')]
let index = items.findIndex(item => item.id == 'item-4')
console.log('item-4 index in visible list is', index)
.hidden {
display: none;
}
<ul id="list">
<li id="item-1">1</li>
<li id="item-2" class="hidden">2</li>
<li id="item-3">3</li>
<li id="item-4">4</li>
<li id="item-5">5</li>
</ul>
Maybe you can use map. First you can create an object with id and value. Then use map function to create array of this object. Then you can access it with foreach, when id = 'item-3'.

How can I create json output from deep li structure

I have ol li structure as html and I want to create JSON from that but my code doesn't create that JSON I need. Can any one please help me to solve it?
I need to create JSON like that
[
{"en":"Menu1","enlink":"#enlink1","tr":"Menü 1","trlink":"#trlink1","data":[
{"en":"Menu1-1","enlink":"#enlink1-1","tr":"Menü 1-1","trlink":"#trlink1-1","data":[
{"en":"Menu1-1-1","enlink":"#enlink1-1-1","tr":"Menü 1-1-1","trlink":"#trlink1-1-1"},
{"en":"Menu1-1-2","enlink":"#enlink1-1-2","tr":"Menü 1-1-2","trlink":"#trlink1-1-2"}
]},
]},
{"en":"Menu2","enlink":"#enlink2","tr":"Menü 2","trlink":"#trlink2","data":[
{"en":"Menu2-1","enlink":"#enlink2-1","tr":"Menü 2-1","trlink":"#trlink2-1"},
{"en":"Menu2-1","enlink":"#enlink2-1","tr":"Menü 2-1","trlink":"#trlink2-1"}
]},
{"en":"Menu3","enlink":"#enlink3","tr":"Menü 3","trlink":"#trlink3"}
]
And my sample codes are..
var buildJson = function (root){
if(!root){
root='#domenu-en';
}
var result = [];
$(' ol > li ',root).each(function() {
if($(this).children("ol").length){
result.push({"en":$(this).attr("data-en"),"data":buildJson($(this))});
//return false;
} else{
result.push({"en":$(this).attr("data-en")});
}
});
return result;
}
$('#results').val(JSON.stringify(buildJson()))
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="domenu-en">
<ol>
<li data-enlink="#enlink1" data-en="Menu1" data-trlink="#trlink1" data-tr="Menü 1">
<ol>
<li data-enlink="#enlink1-1" data-en="Menu1-1" data-trlink="#trlink1-1" data-tr="Menü 1-1">
<ol>
<li data-enlink="#enlink1-1-1" data-en="Menu1-1-1" data-trlink="#trlink1-1-1" data-tr="Menü 1-1-1">
</li>
<li data-enlink="#enlink1-1-2" data-en="Menu1-1-2" data-trlink="#trlink1-1-2" data-tr="Menü 1-1-2">
</li>
</ol>
</li>
</ol>
</li>
<li data-enlink="#enlink2" data-en="Menu2" data-trlink="#trlink2" data-tr="Menü 2">
<ol>
<li data-enlink="#enlink2-1" data-en="Menu2-1" data-trlink="#trlink2-1" data-tr="Menü 2-1">
</li>
<li data-enlink="#enlink2-1" data-en="Menu2-1" data-trlink="#trlink2-1" data-tr="Menü 2-1">
</li>
</ol>
</li>
<li data-enlink="#enlink3" data-en="Menu3" data-trlink="#trlink3" data-tr="Menü 3">
</li>
</ol>
</div>
<textarea id="results" style=" height: 279px;"></textarea>
I found the solution its very simple, in case any one needed I like to share it. If we tell the jQuery select first li as ol:first >li then its generate same hierarchy as li structure.
var buildJson = function (root){
if(!root){
root='#domenu-en';
}
var result = [];
$('ol:first > li ',root).each(function() {
var itemdata = {};
$.each($(this).data(), function(key, value) {
itemdata[key] = value;
});
if($(this).children("ol").length){
itemdata["data"] = buildJson($(this));
}
result.push(itemdata);
});
return result;
}
The main difference seem to be that you don't include the other data- attributes other than the data-en. You can iterate all of the data- attributes on an element with $.each($(this).data(), function(key, value) {}).
In your case that would be something like:
$(' ol > li ',root).each(function() {
var itemdata = {};
$.each($(this).data(), function(key, value) {
itemdata[key] = value;
});
if($(this).children("ol").length){
itemdata["data"] = buildJson($(this));
}
result.push(itemdata);
});
That should give you an equal JSON result. The properties may be in a different order, but that shouldn't matter for JSON (for example: your JSON example had the order en, enlink, tr and trlink, while I got tr, trlink, en and enlink in my example. The order of the menu items in the list is still the same (1, 1-1, 1-2, etc.).
A working example at https://jsfiddle.net/gpctm9d1/

How to match list items with values in an array

I have an array, it contains values [1,2].
I also have an html list
<ul>
<li id="1">List Item 1</li>
<li id="2">List Item 2</li>
<li id="3">List Item 3</li>
</ul>
I need to iterate through the array, and if a value in the array matches an ID in my list, add a class to the list item.
The output example would be
<ul>
<li id="1" class="active">List Item 1</li>
<li id="2" class="active">List Item 2</li>
<li id="3">List Item 3</li>
</ul>
I'm a bit lost on this one, thanks in advance!
Try to use $.map() to translate the array into "#1,#2" and pass it as a selector then add class to it,
var arr = [1,2]; // var arr = Express.completedSteps;
$($.map(arr,function(val,_){
return "#" + val;
}).join()).addClass('active');
DEMO
try
var arr=[1,2];
for(var i in arr){
$("#"+arr[i]).addClass("active");
}
DEMO
Try out following.
$(document).ready(function(){
var obj = [1,2,5,7];
$.each( obj, function( key, value )
{
$('li').each(function(){
if($(this).attr('id')==value)
{
$('#'+value).addClass( "active" );
}
});
});
})
try out the demo here.
demo

Jquery Mobile Duplicating Autodividers

I have a list but when I try to generate autodividers for that list I'm getting duplicate dividers. Here is the code for the ul and the relevant script:
<div data-role="content">
<ul data-role="listview" id="ScheduleList" data-autodividers="true">
<li time="3:30PM">Event 1</li>
<li time="3:30PM">Event 2</li>
<li time="4:30PM">Event 3</li>
<li time="3:30PM">Event 4</li>
<li time="3:30PM">Event 5</li>
<li time="4:30PM">Event 6</li>
</ul>
</div>
</div>
<script>
$(document).on("pageinit", "#ScheduleDay", function(){
$("#ScheduleList").listview({
autodividers: true,
autodividersSelector: function (li) {
var out = li.attr('time');
return out;
}
}).listview('refresh');
});
</script>
Here is the code in JSFiddle: http://jsfiddle.net/4fGT6/65/
I know that I could reorder the list items in the html and that would eliminate the duplicate autodividers, but if I made the list to be generated dynamically from user inputs then I couldn't manually reorder the html.
Is there a way to solve this if the list had been generated dynamically?
Thanks.
First step, sort list items based on data-time attribute (I added data to facilitate reading values - data attribute is ignored by user agent, thus it won't mess up your code).
I used the below simple code, yet genius, made by #undefined.
Update:
Thanks to #Keir Lavelle for reviewing the code of sorting li elements.
var listview = $('#ScheduleList'),
listitems = listview.children('li');
listitems.detach().sort(function (a, b) {
var adata = $(a).data('time');
var bdata = $(b).data('time');
/* return (adata > bdata) ? (adata > bdata) ? 1 : 0 : -1; */
return (adata > bdata) ? 1 : -1;
});
listview.append(listitems);
Second step, apply autodividers dynamically.
$("#ScheduleList").listview({
autodividers: true,
autodividersSelector: function (li) {
var out = li.jqmData('time');
return out;
}
}).listview('refresh');
Demo
Credits to #undefined and #Keir Lavelle

Serializing unordered lists using jquery

I have used Lists within a list and I intend to send the id's using jquery to another php file(updateDB.php).
I tried serializing the list but couldn't get the data. I'm not sure if i've got it right, tried looking around every place but couldn't figure out what's wrong with the code.
<ul>
<li id="recordsArray_<?some number?>"><?php echo content?>`
<ul>
<?php
while (some condition) {
?>
<div>
<li id="subArray_<another number?>"><?php echo content?></li>
</div>
<?php
//conditions - increment within the loop
}
?>
</ul>
</li></ul>
the jquery code is something like this,
$(document).ready(function() {
$(function() {
$("#contentLeft ul").sortable({opacity: 0.6, cursor: 'move', update: function() {
var order = $(this).sortable('serialize') + '&action=updateMenuListings';
$.post("updateDB.php", order, function(theResponse) {
$("#contentRight").html(theResponse);
});
}});
});
$(function() {
$("#contentLeft ul li ul").sortable({opacity: 0.6, cursor: 'move', update: function() {
var order = $(this).sortable('serialize') + '&action=updateSubListings';
$.post("updateDB.php", order, function(theResponse) {
$("#contentRight").html(theResponse);
});
}});
});
});
the id's contentLeft are just the id's for before the lists.
I intend to make it draggable hence used sortable.
On debugging, i'm unable to get any id's of the lists in the variable 'order'.
Please do check out the code and help out.
Thanks
Below you'll find some HTML that is structure like yours along with the JavaScript and jQuery to do what you want. This allows you to move the top level items, with all their respective subitems or just rearrange the subitems. Both options generate a body that you can use in the commented out post line.
You can see it in action here: http://jsfiddle.net/WTjsG/
HTML:
<div id="ContentLeft">Sortable With Update
<ul id="Nav" class="sortable navLevel1">
<li id="Nav_1">Nav1
<!-- NOTE: format id's for UL and LI items as <name>_<int> for serialize to work properly -->
<ul id="NavRecords_1" class="sortable navLevel2">
<li id="Nav1_1">Nav1 Item1</li>
<li id="Nav1_2">Nav1 Item2</li>
<li id="Nav1_3">Nav1 Item3</li>
<li id="Nav1_4">Nav1 Item4</li>
</ul>
</li>
<li id="Nav_2">Nav2
<ul id="NavRecords_2" class="sortable navLevel2">
<li id="Nav2_1">Nav2 Item1</li>
<li id="Nav2_2">Nav2 Item2</li>
<li id="Nav2_3">Nav2 Item3</li>
<li id="Nav2_4">Nav2 Item4</li>
</ul>
</li>
</ul>
</div>
JavaScript w/ jQuery:
$(document).ready(function () {
var isDebugMode = true;
//Set common sort settings for all lists
$(".sortable").sortable({
opacity: 0.6,
cursor: 'move'
});
//Function used to configure update calls for each sort
function setSortAction(selector, updatePage, updateAction, itemLabel) {
$(selector).sortable({
update: function () {
var itemList = $(this).sortable(
"serialize", {
attribute: "id",
key: itemLabel
});
//Create POST request to persist the update
var bodyContent = "action=" + updateAction + "&" + itemList;
if (isDebugMode) { alert("DEBUG: bodyContent = \n" + bodyContent); }
//$.post(updatePage, bodyContent, function (postResult) { alert(postResult); });
}
});
}
//Set sort update action for top level and second level
setSortAction(".navLevel1", "updateDB.php", "updateMenuListings", "record");
setSortAction(".navLevel2", "updateDB.php", "updateMenuItemListings", "record");
});

Categories

Resources