Create a menu tree using JavaScript? - javascript

I have an requirement to create a tree structure menu from a . separated string array.
My out put should be like following
A
A1
A2
A3
B1
B2
B
B1
B2
B3
B4
C
C1
C2
C4
I am trying to implement that with the following script
<script>
function fnAppend(param) {
var cWin=window.open("","Categories","width=600,height=800,scrollbars=yes");
var array = ['A.A1.A2', 'A.A1.A3', 'A.B1.B2','C.C1.C2','B.B1.B2','B.B1.B3.B4','C4'],
tree = [],
levels = [tree],
last = [];
array.forEach(s => {
var temp = s.split('.');
temp.forEach((name, i) => {
if (last[i] === name) return;
levels[i].push({ name, children: levels[i + 1] = [] });
});
last = temp;
});
function traverse(arr) {
for (const branch of arr) {
console.log('Mother:', branch.name);
var ul=document.createElement('ul');
var li=document.createElement('li');
li.innerHTML="<a href=''>"+branch.name+"</a>";
ul.appendChild(li);
if (Array.isArray(branch.children) && branch.children.length > 0) {
console.log('Children:', branch.children.map(i => i.name).join(', '));
traverse(branch.children);
}
}
var list = document.createElement('div');
list.appendChild(ul);
try {
cWin.document.body.appendChild(list);
} catch (err) {
if (list.outerHTML) {
cWin.document.body.innerHTML = list.outerHTML;
} else {
console.log(err.name);
}
}
}
traverse(tree);
}
</script>
</head>
<body>
<ul id="mylist">
</ul>
<input type="button" value="Append child" onclick="fnAppend('test')" />
</body>
Using above script I am getting the following output
A3
B2
B1
C2
C1
B4
B3
B1
C4
Please help me to format the output.
Many thanks in advance.
Regards,
Pankaj

You could create tree and build a nested list with items.
function buildList(tree, target) {
var ul = document.createElement('ul');
tree.forEach(node => {
var li = document.createElement('li');
li.appendChild(document.createTextNode(node.name));
node.children.length && buildList(node.children, li);
ul.appendChild(li);
});
target.appendChild(ul);
}
var array = ['A.A1.A2', 'A.A1.A3', 'A.B1.B2','C.C1.C2','B.B1.B2','B.B1.B3.B4','C4'],
tree = [];
array.forEach(s => s.split('.').reduce((t, name) => {
var temp = t.find(o => o.name === name);
if (!temp) {
t.push(temp = { name, children: [] });
}
return temp.children;
}, tree));
console.log(tree);
buildList(tree, document.body);
A different approach wich works only if pop ups are allowed.
function open() {
var cWin = window.open("", "Categories", "width=600,height=800,scrollbars=yes");
cWin.document.write(buildList(tree).outerHTML);
}
function buildList(tree) {
if (!tree.length) return;
var ul = document.createElement('ul');
tree.forEach(node => {
var li = document.createElement('li'),
children = buildList(node.children);
li.appendChild(document.createTextNode(node.name));
if (children) li.appendChild(children);
ul.appendChild(li);
});
return ul;
}
var array = ['A.A1.A2', 'A.A1.A3', 'A.B1.B2','C.C1.C2','B.B1.B2','B.B1.B3.B4','C4'],
tree = [];
array.forEach(s => s.split('.').reduce((t, name) => {
var temp = t.find(o => o.name === name);
if (!temp) {
t.push(temp = { name, children: [] });
}
return temp.children;
}, tree));
console.log(tree);
<button onclick="open()">popup</button>

Related

how to add a element with href on li element?

i have a search system on my website it works but i can not click on items i search. its a simple script that creates li elements dephended on my search input but how can i add this elements a custom links? because if you can not click on item you searched that does not make any sense... i want to add this const people elements custom links.code:
const people = [
{name:'გადაარჩინე დედამიწა'},
{name:'ანტიმელა'},
{name:'mr.capsule'},
{name:'capsule battle royale'}
];
const list = document.getElementById('list');
function setlist (group) {
clearlist();
for (const person of group) {
const item = document.createElement("li");
item.classList.add("list-group-item");
const text = document.createTextNode(person.name);
item.appendChild(text);
list.appendChild(item);
}
if (group.length === 0) {
setnoresults();
}
}
function clearlist () {
while (list.firstChild) {
list.removeChild(list.firstChild);
}
}
function getrelevency (value, searchTerm) {
if (value === searchTerm) {
return 2;
}else if (value.startsWith(searchTerm)) {
return 1;
}else if (value.includes(searchTerm)) {
return 0;
}
}
function setnoresults () {
const item = document.createElement('li');
item.classList.add('list-group-item');
const text = document.createTextNode('შედეგები ვერ მოიძებნა... სცადეთ თავიდან');
item.appendChild(text);
list.appendChild(item);
}
const searchInput = document.getElementById('search');
searchInput.addEventListener('input', (event) => {
let value = event.target.value;
if (value && value.trim().length > 0) {
value = value.trim().toLowerCase();
setlist(people.filter(person => {
return person.name.includes(value);
}).sort((personA, personB) => {
return getrelevency(personB.name, value) - getrelevency(personA.name, value);
}));
}else {
clearlist();
}
});
You should add it in setlist function.
function setlist (group) {
clearlist();
for (const person of group) {
const item = document.createElement("li");
item.classList.add("list-group-item");
const link = document.createElement("a");
link.href = "http://..."; // put here where the link should point
item.appendChild(link);
const text = document.createTextNode(person.name);
link.appendChild(text);
list.appendChild(item);
}
if (group.length === 0) {
setnoresults();
}
}
i got solution with this way checking what search input is and if its targeted search it will add a link on custom search. if statemant is doing everything!
function setlist (group) {
clearlist();
for (const person of group) {
const item = document.createElement("li");
item.classList.add("list-group-item");
if (person.name === "გადაარჩინე დედამიწა") {
const link = document.createElement("a");
link.href = "https://google.com"; // put here where the link should point
link.text = "გადაარჩინე დედამიწა"
item.appendChild(link);
}
const text = document.createTextNode(person.name);
item.appendChild(text);
list.appendChild(item);
}
if (group.length === 0) {
setnoresults();
}
}

How do I recursively reverse a linked list?

Input: 5 -> 9 -> 8 -> 3 -> 1 -> 7
Expected Output: 7 -> 1 -> 3 -> 8 -> 9 -> 5
Issue:
When I display the reversed linked list the result is 5. This is an issue because this should be the tail and not the head. The rest of the nodes are missing in the display well.
Question:
Is there an issue with the code base that is preventing the traversal from the head to the tail and changing the pointers to reverse the list?
Code:
LinkedList:
class LinkedList {
constructor() {
this.head = null;
this.size = 0;
}
insertFirst(item) {
if (this.head !== null) {
const newHead = new _Node(item);
let oldHead = this.head;
oldHead.prev = newHead;
newHead.next = oldHead;
this.head = newHead;
} else {
this.head = new _Node(item, this.head);
}
this.size++;
}
insertLast(item) {
if (!this.head) {
this.insertFirst(item);
} else {
let tempNode = this.head;
while (tempNode.next !== null) {
tempNode = tempNode.next;
}
tempNode.next = new _Node(item, null, tempNode);
}
this.size++
}
}
module.exports = LinkedList;
Main:
const LinkedList = require("./LinkedLists");
const { reverse } = require("./Reverse");
const { display } = require("./Supplemental");
function main() {
let SLL = new LinkedList();
SLL.insertFirst(5);
SLL.insertLast(9);
SLL.insertLast(8);
SLL.insertLast(3);
SLL.insertLast(1);
SLL.insertLast(7);
reverse(SLL);
display(SLL);
return SLL;
}
console.log(main());
Reverse:
reverse = (SLL) => {
let curr = SLL.head
if (!curr) {
return;
}
if (!curr.next) {
SLL.head = curr;
return;
}
let tmp = reverse(curr.next);
curr.next.next = curr;
curr.next = null;
return tmp;
}
module.exports = { reverse };
Display:
display = (SLL) => {
let currentNode = SLL.head;
if (!SLL.head) {
return null;
}
while (currentNode !== null) {
console.log(currentNode.value);
currentNode = currentNode.next;
}
return;
};
Can someone tell me what return tmp is doing in the Reverse.js file?
(1) Removed display from Main.js
(2) Edited Main.js:
const LinkedList = require("./LinkedLists");
const { reverse } = require("./Reverse");
const { display } = require("./Supplemental");
function main() {
let SLL = new LinkedList();
SLL.insertFirst(5);
SLL.insertLast(9);
SLL.insertLast(8);
SLL.insertLast(3);
SLL.insertLast(1);
SLL.insertLast(7);
const result = reverse(SLL.head);
console.log(result);
return SLL;
}
return main();
(3) Edited Reverse.js:
reverse = (curr, prev = null) => {
if (!curr) {
return prev;
}
let tmp = reverse(curr.next, curr);
const temp = curr.next;
curr.next = prev;
curr.prev = temp;
return tmp;
}
module.exports = { reverse };

Create Html Tree view with native javascript / HTML

I need to create an HTML/CSS tree view as in the example from already created object using native javascript.
Please suggest,
BR
You could first build nested structure and then use recursive approach to also create html from that data where if the current element has children property you call the function again with that children array as a data parameter.
var data = [{"name":"container-1","type":"container","description":"container description"},{"name":"category-1","type":"category","parent":"container-1"},{"name":"grid-1","type":"grid","parent":"category-1"},{"name":"chart-1","type":"chart","parent":"category-1"},{"name":"container-2","type":"container"},{"name":"category-2","type":"category","parent":"container-2"},{"name":"category-3","type":"category","parent":"container-2"},{"name":"grid-2","type":"grid","parent":"category-2"},{"name":"chart-2","type":"chart","parent":"category-2"},{"name":"grid-3","type":"grid","parent":"category-3"}]
function toTree(data, pid = undefined) {
return data.reduce((r, e) => {
if (pid == e.parent) {
const obj = { ...e }
const children = toTree(data, e.name)
if (children.length) obj.children = children;
r.push(obj)
}
return r
}, [])
}
function toHtml(data, isRoot = true) {
const ul = document.createElement('ul')
if (!isRoot) {
ul.classList.add('hide')
}
data.forEach(e => {
let isVisible = isRoot;
const li = document.createElement('li')
const text = document.createElement('span')
const button = document.createElement('button')
if (e.children) {
button.textContent = '+'
li.appendChild(button)
}
text.textContent = e.name
li.appendChild(text)
if (e.children) {
const children = toHtml(e.children, false)
li.appendChild(children)
button.addEventListener('click', function() {
if (isRoot) {
isVisible = !isVisible
}
button.textContent = isVisible ? '+' : '-'
children.classList.toggle('hide')
if (!isRoot) {
isVisible = !isVisible
}
})
}
ul.appendChild(li)
})
return ul;
}
const tree = toTree(data)
const html = toHtml(tree)
document.body.appendChild(html)
.hide {
display: none;
}
button {
margin-right: 10px;
}

JSON menu alphabetical order

Based on this tutorial i made JSON menu. And now items sorted by id, but i need sort them alphabetically and can't understand how to do that. Not sure if array.sort(); will help in my case
Here is JS code:
var builddata = function () {
var source = [];
var items = [];
for (i = 0; i < data.length; i++) {
var item = data[i];
var label = item["text"];
var parentid = item["parentid"];
var id = item["id"];
if (items[parentid]) {
var item = { parentid: parentid, label: label, item: item };
if (!items[parentid].items) {
items[parentid].items = [];
}
items[parentid].items[items[parentid].items.length] = item;
items[id] = item;
}
else {
items[id] = { parentid: parentid, label: label, item: item };
source[id] = items[id];
}
}
return source;
}
var source = builddata();
var buildUL = function (parent, items) {
$.each(items, function () {
if (this.label) {
var li = $("<li>" + this.label + "</li>");
li.appendTo(parent);
if (this.items && this.items.length > 0) {
var ul = $("<ul></ul>");
ul.appendTo(li);
buildUL(ul, this.items);
}
}
});
}
var ul = $("<ul></ul>");
ul.appendTo("#jqxMenu");
buildUL(ul, source);
HTML:
<div id='jqxMenu'>
</div>
Here is JSFIDDLE
source.sort(function(a, b){
if(a.text < b.text) return -1;
if(a.text > b.text) return 1;
return 0;
})
...is the basis. However, recursive implementation is needed in this case I suppose.
Here: jsfiddle
Try sort the source u return
return source.sort();
See: DEMO

Find an element in an array recursively

I have an array of objects. Every object in the array has an id and an item property that is an array containing other object. I need to be able to find an element in an array by id. Here is a sample of what I have done so far, but the recursive function is always returning undefined.
How can I quit the function and return the item when I have called the function recursively several times?
$(function () {
var treeDataSource = [{
id: 1,
Name: "Test1",
items: [{
id: 2,
Name: "Test2",
items: [{
id: 3,
Name: "Test3"
}]
}]
}];
var getSubMenuItem = function (subMenuItems, id) {
if (subMenuItems && subMenuItems.length > 0) {
for (var i = 0; i < subMenuItems.length; i++) {
var item;
if (subMenuItems[i].Id == id) {
item = subMenuItems[i];
return item;
};
getSubMenuItem(subMenuItems[i].items, id);
};
};
};
var searchedItem = getSubMenuItem(treeDataSource, 3);
alert(searchedItem.id);
});
jsFiddle
You should replace
getSubMenuItem(subMenuItems[i].items, id);
with
var found = getSubMenuItem(subMenuItems[i].items, id);
if (found) return found;
in order to return the element when it is found.
And be careful with the name of the properties, javascript is case sensitive, so you must also replace
if (subMenuItems[i].Id == id) {
with
if (subMenuItems[i].id == id) {
Demonstration
Final (cleaned) code :
var getSubMenuItem = function (subMenuItems, id) {
if (subMenuItems) {
for (var i = 0; i < subMenuItems.length; i++) {
if (subMenuItems[i].id == id) {
return subMenuItems[i];
}
var found = getSubMenuItem(subMenuItems[i].items, id);
if (found) return found;
}
}
};
I know its late but here is a more generic approach
Array.prototype.findRecursive = function(predicate, childrenPropertyName){
if(!childrenPropertyName){
throw "findRecursive requires parameter `childrenPropertyName`";
}
let array = [];
array = this;
let initialFind = array.find(predicate);
let elementsWithChildren = array.filter(x=>x[childrenPropertyName]);
if(initialFind){
return initialFind;
}else if(elementsWithChildren.length){
let childElements = [];
elementsWithChildren.forEach(x=>{
childElements.push(...x[childrenPropertyName]);
});
return childElements.findRecursive(predicate, childrenPropertyName);
}else{
return undefined;
}
}
to use it:
var array = [<lets say an array of students who has their own students>];
var joe = array.findRecursive(x=>x.Name=="Joe", "students");
and if you want filter instead of find
Array.prototype.filterRecursive = function(predicate, childProperty){
let filterResults = [];
let filterAndPushResults = (arrayToFilter)=>{
let elementsWithChildren = arrayToFilter.filter(x=>x[childProperty]);
let filtered = arrayToFilter.filter(predicate);
filterResults.push(...filtered);
if(elementsWithChildren.length){
let childElements = [];
elementsWithChildren.forEach(x=>{
childElements.push(...x[childProperty]);
});
filterAndPushResults(childElements);
}
};
filterAndPushResults(this);
return filterResults;
}

Categories