Serialize from ul li with child using jqueryui sortable - javascript

I would like to know how to create a JSON or serialize (both is fine) from a ul including
<ul class="menu send ui-sortable">
<li id="pageid_1" class="ui-sortable-handle">Inscription
<ul class="menu send ui-sortable">
<li id="pageid_2" class="ui-sortable-handle">Joueurs en ligne
<ul class="menu send ui-sortable"></ul>
</li>
</ul>
</li>
I cannot find how to create something like this:
pageid[]=1&pageid[1]=2 OR [{"pageid":1,"children":[{"pageid":2}]}]
Meaning, including parent ID in [].
Thank you for your help!

This code will produce the output required
var result = [].map.call(document.querySelectorAll('ul.menu.send.ui-sortable li.ui-sortable-handle'), function(li) {
var parent = '';
if (li.parentNode && li.parentNode.parentNode && li.parentNode.parentNode.nodeName == 'LI' && li.parentNode.parentNode.matches('li.ui-sortable-handle')) {
parent = li.parentNode.parentNode.id.replace(/\D+/g, '');
}
return "pageid[" + parent + "]=" + li.id.replace(/\D+/g, '');
}).join('&');
console.log(result); // pageid[]=1&pageid[1]=2
I haven't thought about how to do the second format, because the first format is easier to produce

Related

javascript, nested navbar from json

My aim is to replicate this structure automatically from a json file.
<ul class="sidebar-menu">
<li class="treeview">
Mammals
<ul class="treeview-menu">
<li>Goat</li>
<li> Sheep
<ul class="treeview-menu">
<li>Bone</li>
<li>Bone
<ul class="treeview-menu">
<li>Variant 1</li>
<li> Variant 2</li>
</ul>
</li>
</ul>
</li>
</ul>
</li>
</ul>
JSON
[
{"datasetID":"5fd4124900827","higherClassification":"Eukarya","kingdom":"Animalia","phylum":"Chordata","class":"Mammalia","order":"Artiodactyla","family":"Bovidae","genus":"Capra","subgenus":"None","vernacularName":"goat","commonName":"None","elementName":"Calcaneus","commonElementName":"None"},
{"datasetID":"5fd4058e5c8d2","higherClassification":"Eukarya","kingdom":"Animalia","phylum":"Chordata","class":"Mammalia","order":"Artiodactyla","family":"Bovidae","genus":"Capra","subgenus":"None","vernacularName":"goat","commonName":"goat","elementName":"Femur","commonElementName":"None"}
]
The relevant parts are:
"datasetID":"5fd4124900827"
"class":"Mammalia",
"order":"Artiodactyla",
"family":"Bovidae",
"genus":"Capra",
"subgenus":"None",
"vernacularName":"goat",
"elementName":"Calcaneus"},
So the class is on the top level of the hierarchy, it could be mammal, bird, fish...
Taking class: Mammalia as an example, under this is order under that family under that genus
then if there is a subgenus that is on the next level also.
Under that is the vernacularName then elementName.
Each record has a unique id datasetID there may be multiple "elementName": "Calcaneus" for a goat, these need an integer added (i.e. Calcaneus 1, then Calcaneus 2, then Calcaneus 3 etc.
>Mammalia
>order
>family
>genus
>subgenus (if exists)
>vernacularName
>elementName (if more than one append 1,2,3...)
So, my mega question, how to do this in javascript?
My attempt so far:
Php gets the json, yes this could be done in javascript.
<?php
$data = json_decode(file_get_contents("bonify" . $version . "/app/json/data.json"), True);
?>
Javascript picks up the json:
<script type="text/javascript">
const version = "<?php echo $version; ?>";
$.getJSON('bonify'+ version +'/app/json/data2.json', function(json) {
console.log(json); // this will show the info it in firebug console
obj = json
This lists all the json data:
function printValues(obj) {
for(var k in obj) {
if(obj[k] instanceof Object) {
printValues(obj[k]);
} else {
document.write(obj[k] + "<br>");
};
}
};
closing code:
});
</script>
I'm not convinced document.write is the best way to do this.
I have this code for my search and it seems like I should adapt that but with out the filter capability.
$('#txt-search').keyup(function(){
var searchField = $(this).val();
if(searchField === '') {
$('#filter-records').html('');
return;
}
var regex = new RegExp(searchField, "i");
var output = '<div class="col-12 p-0"><hr />';
var count = 1;
$.each(data, function(key, val){
if ((val.datasetID.search(regex) != -1) || (val.ownerInstitutionCode.search(regex) != -1)|| (val.vernacularName.search(regex) != -1)|| (val.elementName.search(regex) != -1)) {
output += '<ul class="sidebar-menu">';
output += '<li><i class="fas fa-bone" data-fa-transform="rotate-45"></i> <span>' + val.vernacularName + ': ' + val.elementName + '</span></li>';
output += '</ul>';
if(count%2 == 0){
output += '</div>'
}
count++;
}
});
$('#filter-records').html(output);
});
});
});
I'm assuming several nested foreach loops is the way to go? I've put the whole aim for clarity. I am trying to learn and I have a learning disability so please be patient with me, thanks for your help. I've tried to included as much info as possible to avoid having my question closed as too broad.
You have a repeated pattern. If we assume that you have built a hierarchical data structure, then we can use a function using template literals, like:
function buildChildren(children) {
var tvms = [];
for (let child of children) {
tvms.push(myTreeViewMenu(child));
}
return tvms;
}
function myTreeViewMenu(treeViewMenu) {
tvms = buildChildren(treeViewMenu.children);
return `
<ul class="treeview-menu">
<li>${treeViewMenu.name} ${tvms.join("")}</li>
</ul>
`;
}
function myTree(tree) {
tvms = buildChildren(tree.children);
return `
<ul class="sidebar-menu">
<li class="treeview">
${tree.name}
${tvms.join("")}
</li>
</ul>
`;
}
(NOT TESTED)
This logic can be a starting point for you, basically you nest your pattern into itself. You need to make sure that from your raw JSON you build an object tree whose nodes have a string called name and an array for the subtree called children. Also, make sure there are no cycles in the tree.

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/

Javascript create recursive unordered list from ordered lists

I have the following HTML
<ol data-level="0">
<li class="active">This is the data1</li>
</ol>
<ol data-level="1">
<li class="active">This is the data2</li>
</ol>
<ol data-level="2">
<li class="active">This is the data3</li>
</ol>
<ol data-level="3">
<li class="active">This is the data4</li>
</ol>
<ol data-level="2">
<li class="active">This is the data5</li>
</ol>
<ol data-level="2">
<li class="active">This is the data6</li>
</ol>
<ol data-level="0">
<li class="active">This is the data7</li>
</ol>
Below you can see the outcome of the above HTML (I stripped out some html tags for better readability!).
I have to create a valid unordered list from this outcome. I've searched around stackoverflow and I came across multiple solutions. E.g. making use of a recursive function. The problem with this is that I don't know how to pass in the objects that belong to the 'root' object (see: How can I recursively create a UL/LI's from JSON data - multiple layers deep).
At the moment I'm stuck with this code:
var $root = $items.first();
var rootLvl = $root.data('level');
var prevLvl = rootLvl;
function recursiveCheck($next) {
prevLvl = $next.data('level');
var nextItem = $next.next();
if (nextItem.data('level') > prevLvl) {
console.log('check');
recursiveCheck(nextItem);
} else {
console.log('break');
}
}
recursiveCheck($root);
At the 'break' I don't know how to go back to the previous root element.. Can someone pull me in the right direction?
Thanks in advance!
Edit: My desired outcome is like this:
<ul>
<li>Data 1
<ul>
<li> Data 2
<ul>
<li> Data 3
<ul>
<li>
Data 4
</li>
</ul>
</li>
<li>Data 5
</li>
<li>Data 6
</li>
</ul>
</li>
</ul>
</li>
<li>Data 7
</li>
</ul>
More information
- level 1
- - level 2 // this belongs to the first level 1
- - - level 3 // this belongs to level 2
- - - level 3 // this belongs to level 2
- - - level 3 // this belongs to level 2
- - level 2 // this belongs to the first level 1
- level 1
- - level 2 // this belongs to the second level 1
in this example the first level 1 has two level 2 subitems, but one of them also has three level 3 subitems. So I have to find out how to get the next items with a higher level. I'm able to produce above string with '- - -'. I just can't convert this to an unordered list.
Solved, final code:
edu.saveSummary = function() {
var dataLevel = 'edu-draggable-level';
var node = $('[data-edu-draggable]').first();
function parseUL() {
var level = node.data(dataLevel);
var ul = document.createElement("ul");
while (node && node.data(dataLevel) === level) {
var li = document.createElement("li");
li.textContent = node.text();
ul.appendChild(li);
node = node.next();
if (node && +node.data(dataLevel) > level) {
li.appendChild(parseUL());
}
}
return ul;
}
return parseUL();
};
Something along these lines should work:
var node = first_ol_node;
function parseUL() {
var level = +node.children().first().data('level');
var ul = document.createElement("ul");
while (node && +node.children().first().data('level') === level) {
var li = document.createElement("li");
li.textContent = node.text();
ul.appendChild(li);
node = node.next();
if (node && +node.children().first().data(level) > level) {
li.appendChild(parseUL());
}
}
return ul;
}
var ul = parseUL();
One problem is that you need both to return the parsed ul node from the function and you also need to advance the parsing pointer over the source items (and in Javascript there's no way to pass a variable by reference, you can only pass by value).
In this solution I'm using an external variable instead of a parameter to keep the current pointer in the source items (advanced in node = node.next()) and the return value is the parsed UL.

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