I have the following array of objects:
payload: [
{name: one},
{name: two,
values: {name: five}
},
{name: three},
{name: four}
]
I loop through this in a recursive way, because this depth of the data can change anytime. So name: five can have there own values again.
Now when I loop through the values of an object, I want the name of the parent object. So for name: five I want to get two in a method.
Is there any way to obtain this name?
I use vue.js a a Javascript library.
This is my loop:
<ul>
<div class="row">
<li v-if="value.name" class="col-md-3 indent" #click="toggle">
{{value.name}}:
</li>
<li v-else class="col-md-3 indent" #click="toggle">
{{value.type}}:
</li>
</div>
<div v-show="open" v-if="isValue">
<codeblock-value
v-for="value in value.values"
:value="value">
</codeblock-value>
</div>
</ul>
And I render this loop like this in my parent file:
<div class="row" v-for="value in payload.values">
<codeblock-value
:value="value">
</codeblock-value>
</div>
Keep in mind that there can be multiple objects with values.
function recurse(parentName, obj) {
console.log("Parent name is: " + parentName);
console.log("Obj name is: " + obj.name);
if(obj.values) {
recurse(obj.name, obj.values);
}
}
recurse(payload[1]);
If you can change your payload structure slightly it would make life a bit easier.
JSFIDDLE
JS
var payload = {
name: "payload",
values: [{
name: "one"
}, {
name: "two",
values: [{
name: "five"
}]
}, {
name: "three"
}, {
name: "four"
}]
};
function dig(parent) {
console.log(parent.name);
if (parent.hasOwnProperty('values') && Array.isArray(parent.values)) {
for(var x = 0, len = parent.values.length; x < len; x++){
dig(parent.values[x]);
}
}
}
dig(payload);
UPDATE FOR VUE.JS
Again, changing the data structure allows you to access the parent. In this example, i dynamically generate the test data so that each child node references its parent (I threw in some randomness to generate folders or not).
JSFIDDLE
Data generation JS
var data = {
name: 'My Tree',
children: []
}
var maxDepth = 4;
function createChild(parent, currentDepth){
var childrenValues = ['hello', 'wat', 'test'];
var createChildFolderChance = 0.5;
for(var x = 0, len = childrenValues.length; x < len; x++){
var child = {
name: childrenValues[x],
parent: parent
}
if(Math.random() < createChildFolderChance && currentDepth < maxDepth){
child.children = [];
currentDepth++;
createChild(child, currentDepth)
}
parent.children.push(child);
}
}
createChild(data, 0);
Updated Vue.JS click code
function() {
if (this.isFolder) {
this.open = !this.open
}else{
var firstSiblingWithChildren;
// cycle through current node's parent's children (ie. siblings) and return the name of the first node that has children
for(var x = 0, len = this.model.parent.children.length; x < len; x++){
if(this.model.parent.children[x].hasOwnProperty('children') && Array.isArray(this.model.parent.children[x].children)){
firstSiblingWithChildren = this.model.parent.children[x].name;
break;
}
}
console.log(firstSiblingWithChildren);
}
},
Related
I got a problem,each time I add the element,it multiplicates itself(for example if there is 1 element in the cart,on the next click it will add 2 more,and if there are 3 elements,it will add 3 more,if 6,it will add 4 more),that's a huge problem,please help!
My js code:
let pList = document.getElementById("productList");
const addDrip = () => {
let Drip, localObj, tObj;
Drip = {
name: "Traditional Drip",
type: "normal",
price: "2.00",
imgSrc: "images/c5.png"
};
localObj = localStorage.getItem("Drip");
if(!localObj) tObj = [];
else tObj = JSON.parse(localObj);
tObj.push(Drip)
localStorage.setItem("Drip", JSON.stringify(tObj));
for(let i = 0;i < tObj.length;i++){
let htmlDrip = `
<div class="productDiv">
<p>${tObj[i].name}</p>
<p>${tObj[i].type}</p>
<p>${tObj[i].price}$</p>
<img src="${tObj[i].imgSrc}">
</div>
`
document.getElementById("productList").innerHTML += htmlDrip;
}
}
My html part:
<div class="cList">
<img src="images/c5.png"class = "tDrip">
<h1 class="Type">Traditional Drip</h1>
<button id="tAdd"onclick="addDrip()">Add Traditional Drip</button>
</div>
<div id = productList></div>
This is because your array already has the drip update from the localstorage
Simply replace your JS code with this and it will work desired way you want.
let pList = document.getElementById("productList");
const addDrip = () => {
let Drip, localObj, tObj;
Drip = {
name: "Traditional Drip",
type: "normal",
price: "2.00",
imgSrc: "images/c5.png"
};
localObj = localStorage.getItem("Drip");
console.log(localObj,"Local object")
if(!localObj) {
tObj = [];
tObj.push(Drip)
}else {
tObj = JSON.parse(localObj);
console.log(tObj,"T object")
}
localStorage.setItem("Drip", JSON.stringify(tObj));
for(let i = 0;i < tObj.length;i++){
let htmlDrip = `
<div class="productDiv">
<p>${tObj[i].name}</p>
<p>${tObj[i].type}</p>
<p>${tObj[i].price}$</p>
<img src="${tObj[i].imgSrc}">
</div>
`
document.getElementById("productList").innerHTML += htmlDrip;
}
}
you just have to push when your array is empty. Give it a try it will work.
NOTE : you can check the demo too. Just clear the localstorage if you refresh.https://www.w3schools.com/code/tryit.asp?filename=GS7FA0970MF3
You don't actually need to loop thru the array while adding the new item.
You can first get the items from localStorage
HTML:
<div class="cList">
<img src="images/c5.png" class="tDrip">
<h1 class="Type">Traditional Drip</h1>
<button id="tAdd" onclick="addDrip()">Add Traditional Drip</button>
</div>
<div id="productList"></div>
Script:
const pList = document.getElementById("productList");
let localObj = JSON.parse(localStorage.getItem("Drip"));
const parseItems = () => {
if (!localObj) localObj = [];
for (let i = 0; i < localObj.length; i++) {
let htmlDrip = `
<div class="productDiv">
<p>${localObj[i].name}</p>
<p>${localObj[i].type}</p>
<p>${localObj[i].price}$</p>
<img src="${localObj[i].imgSrc}">
</div>
`;
pList.innerHTML += htmlDrip;
}
}
parseItems();
const addDrip = () => {
let Drip;
Drip = {
name: "Traditional Drip",
type: "normal",
price: "2.00",
imgSrc: "images/c5.png"
};
if (!localObj) localObj = [];
localObj.push(Drip);
localStorage.setItem("Drip", JSON.stringify(localObj));
let htmlDrip = `
<div class="productDiv">
<p>${Drip.name}</p>
<p>${Drip.type}</p>
<p>${Drip.price}$</p>
<img src="${Drip.imgSrc}">
</div>
`;
pList.innerHTML += htmlDrip;
}
I'm making a movie sorter list, you enter the title and then the rating and it will show you the movies in order by rating. I have an array of objects and I managed to sort the array by rating, but I can't find a way to actually display the array in order on the HTML DOM.
I've tried for loops and forEach's but they don't work the way I want.
const movieTitle = document.querySelector(".movie-title");
const movieRating = document.querySelector(".movie-rating");
const movieList = document.querySelector(".movie-list");
const sortBtn = document.querySelector(".btn");
let movieStorage = [];
function sendMovie() {
if(event.keyCode == 13) {
if(movieTitle.value != "" && movieRating.value != "") {
title = movieTitle.value;
rating = parseInt(movieRating.value);
movieStorage.push({
title: title,
rating: rating
});
// If rating of a is bigger than rating of b return 1, if not return -1
movieStorage.sort((a, b) => (a.rating > b.rating) ? -1 : 1);
console.log(movieStorage);
addMovieToList(title, rating);
movieTitle.value = "";
movieRating.value = "";
} else {
console.log("Fields missing");
}
}
}
function addMovieToList(title, rating) {
const div = document.createElement("div");
div.className = "list-items";
div.innerHTML = `
<div class="item-title">
<p>${title}</p>
</div>
<div class="item-rating">
<p>${rating}</p>
</div>
<div class="item-delete">
<i class="fa fa-trash trash-icon delete"></i>
</div>
`;
movieList.appendChild(div);
}
function sortByRating(element) {
for(let i = 0; i < movieStorage.length; i++) {
element.innerHTML = `
<div class="item-title">
<p>${movieStorage[i].title}</p>
</div>
<div class="item-rating">
<p>${movieStorage[i].rating}</p>
</div>
<div class="item-delete">
<i class="fa fa-trash trash-icon delete"></i>
</div>
`;
}
}
document.addEventListener("click", (e) => {
const deleteIcon = e.target;
const item = document.querySelector(".list-items");
if(deleteIcon.classList.contains("delete")) {
deleteIcon.parentElement.parentElement.remove(item);
}
})
tldr demo
After sorting the array, you need a way to reference movie divs to sort them. There are many ways to do it, what I chose is using id. When you create movie <div>, give it an ID unique for each movie name:
// Simple function to generate hash number for each string
function hashStr(stringValue) {
var hash = 0, i, chr;
if (stringValue.length === 0) return hash;
for (i = 0; i < stringValue.length; i++) {
chr = stringValue.charCodeAt(i);
hash = ((hash << 5) - hash) + chr;
hash |= 0; // Convert to 32bit integer
}
return hash;
}
const MOVIES = [
{name: "a", rating: 3},
{name: "b", rating: 6},
{name: "c", rating: 3},
{name: "d", rating: 2},
{name: "e", rating: 1},
];
function showMovies() {
const moviesDiv = document.querySelector("#movies");
for(const movie of MOVIES)
{
const id = "movie-"+hashStr(movie.name);
// If there's no element with the ID, we need to create the DIV for the movie
if(!document.querySelector("#"+id)) {
const elm = document.createElement("div");
elm.appendChild(new Text(movie.name + " ("+movie.rating+"/10)"));
elm.id = id;
elm.classList.add("movie");
moviesDiv.appendChild(elm);
}
}
}
Then, when sorting, you can reference each movie by ID:
// Sort movies using given property (eg. "name")
// The second param determines sort direction
function sortBy(property, ascending=true) {
MOVIES.sort((a,b) =>{
return cmp(a[property], b[property], ascending);
});
// Now after sorting the array, we can sort the HTML elements
const moviesDiv = document.querySelector("#movies");
let lastMovie = null;
for(const movie of MOVIES)
{
const id = "#movie-"+hashStr(movie.name);
const movieDiv = document.querySelector(id);
console.log(id, movieDiv);
// If created
if(movieDiv) {
// remove and append after last processed movie (for the first movie, this will append to top)
moviesDiv.insertBefore(movieDiv, lastMovie);
}
}
}
// Compare string and number, makes no sense for other types
function cmp(a,b, ascending=true) {
if(typeof a=='number' && typeof b == "number") {
return ascending ? a-b : b-a;
}
else if(typeof a=='string' && typeof b == "string"){
return (ascending ? 1 : -1) * a.localeCompare(b);
}
else {
return 0;
}
}
When you add a movie, you just call sort again. You will need to remember the last sorting parameters for that.
Your sort will work fine. The problem is that after you've sorted you can't just display that movie, you have to redisplay the entire list. You're almost there with your sortByRating method, but it doesn't recreate the entire list correctly. Try something like:
function showMoviesList(element) {
let innerHTML = "";
for (let i = 0; i < movieStorage.length; i++) {
innerHTML += `
<div class="item-title">
<p>${movieStorage[i].title}</p>
</div>
<div class="item-rating">
<p>${movieStorage[i].rating}</p>
</div>
<div class="item-delete">
<i class="fa fa-trash trash-icon delete"></i>
</div>
`;
}
element.innerHTML = innerHTML;
}
This resets the inner HTML of the element to the complete movie list in order every time it's called.
Now call showMoviesList(movieList) instead of calling addMovieToList in sendMovie.
HTML:
<section class="cd-gallery">
<ul id="courses">
</ul>
<div class="cd-fail-message">No results found</div>
</section>
<ul>
<li><input id="buttonaz" type="button" value="Course name(a-z)"/></li>
<li><input id="buttonza" type="button" value="Course name(z-a)"/></li>
<li><input id="buttonlu" type="button" value="Last updated"></li>
<ul>
JavaScript:
var public_spreadsheet_url = 'https://docs.google.com/spreadsheets/..."
function init() {
Tabletop.init( { key: public_spreadsheet_url,
callback: showInfo,
simpleSheet: true } );
}
window.addEventListener('DOMContentLoaded', init);
function sortAZ(a, b) {
var x = a.Course.toLowerCase();
var y = b.Course.toLowerCase();
return ((x < y) ? -1 : ((x > y) ? 1 : 0));
}
function sortZA(a, b) {
var x = a.Course.toLowerCase();
var y = b.Course.toLowerCase();
return ((x > y) ? -1 : ((x < y) ? 1 : 0));
}
function showInfo(data) {
var bodyContent = '';
var sheetUrlRoot = 'https://docs.google.com/spreadsheets/d/';
var buttonaz = document.getElementById("buttonaz");
var buttonza = document.getElementById("buttonza");
console.log(data)
for (var i = 0; i < data.length; i++) {
var sheetUrl = sheetUrlRoot + data[i].ActionId;
var c = data[i].Course;
var courseName = '<div class=\"courseName\">' + c + '</div>';
var designer = data[i]['Designer'].toLowerCase();
var numHolds = data[i]['On Hold']
if (numHolds > 0) {
bodyContent += '<li class="color-2 course mix ' + designer + ' "style="background-color: #E89696";>' + courseName + statusList+ sheetLink+ '</li>';
} else if (numHolds <= 0){
bodyContent += '<li class="color-1 course mix ' + designer + ' "style="background-color: #C2D5BE";>' + courseName + statusList+ sheetLink+'</li>';
}
}
document.getElementById('courses').innerHTML = bodyContent;
document.getElementById('buttonaz').onclick = data.sort(sortAZ);
document.getElementById('buttonaz').onclick = data.sort(sortZA);
}
Hi Stack Overflow users,
I have imported data using tabletop.js to display a set of courses that my university has in hand. However, I cannot have it to display the courses sorting alphabetically from a-z, as well as from z-a when the buttons "Course name (a-z)" and "Course name (z-a)" are clicked. The data are displayed when the page is first loaded, but will not do anything when I click the sorting buttons.
Please help and any input will be appreciated!
P.S. I'm also filtering the courses by the name of designer using mixitup jQuery plugin.
Refer the code which have two button , one is for sort aZ and one is for sort Za . Click on Expand snippet , you will see two button , click on them and enjoy sorting
<!DOCTYPE html>
<html>
<body>
<h2>JavaScript Array Sort</h2>
<p>Click the buttons to sort the array alphabetically or numerically.</p>
<button onclick="myFunction1()">Sort Az</button>
<button onclick="myFunction2()">Sort zA</button>
<p id="demo"></p>
<script>
var points = ["z", "b", "d", "a"];
var data1=Array.prototype.slice.call(points);
console.log('Za Sort ',data1.sort().reverse());
document.getElementById("demo").innerHTML = points;
function myFunction1() {
points.sort();
document.getElementById("demo").innerHTML = points;
}
function myFunction2() {
document.getElementById("demo").innerHTML = data1.sort().reverse();
}
</script>
</body>
</html>
If incoming data is array use javascript built in sort() function to sort data
var data = ["z", "b", "d", "a"];
data.sort();
console.log('Ascending order aZ ',data)
data.reverse();
console.log('Descending order zA',data);
output
Ascending order ["a", "b", "d", "z"]
Descending order["z", "d", "b", "a"]
If you want to use library https://underscorejs.org/#
var stooges = [{name: 'moe', age: 40}, {name: 'larry', age: 50}, {name: 'curly', age: 60}];
_.sortBy(stooges, 'name');
Suppose I have two really long lists of People, ~20,000 each, but only one list is displayed at a given time. Each Person has a Name, Nickname, and Age. The goal is to display each and every person in a list that can be scrolled, sorted, and searched efficiently. In addition, I need to be able to swap between lists. I have been able to create the desired functionality, but the individual operations are not very user friendly. The scrolling is not smooth and sorting/searching/swapping can freeze the UI for a second or two, and sometimes the page will even crash. Therefore I am looking for improvements on the following code, and am wondering if a smooth user experience is even possible with lists of this size without having to use virtual rendering (only populating the DOM with what is visible by the viewport and faking the scroll bar). Thanks in advance!
The Javascript is structured as:
var groupOne =
[
{name: "Michael", nickname: "Mike", age: 23},
{name: "Matthew", nickname: "Matt", age: 25},
{name: "Aaron", nickname: "Ron", age: 30},
...
]
var groupTwo =
[
{name: "Joseph", nickname: "Joe", age: 20},
{name: "Carter", nickname: "C", age: 30},
{name: "William", nickname: "Will", age: 40},
...
]
A Person is displayed as:
---------------------------------
| Name | |
-------------------------| Age |
| Nickname | |
---------------------------------
The HTML is structured as:
<input id="search" type="text">
<button id="sort"></button>
<button id="swap"></button>
<ol id="people">
<li class="person" data-name="Michael" data-nickname="Mike" data-age="23">
<p class="name">Michael</p>
<p class="nickname">Mike</p>
<p class="age">23</p>
</li>
<li class="person" data-name="Matthew" data-nickname="Matt" data-age="25">
<p class="name">Matthew</p>
<p class="nickname">Matt</p>
<p class="age">25</p>
</li>
<li class="person" data-name="Aaron" data-nickname="Ron" data-age="30">
<p class="name">Aaron</p>
<p class="nickname">Ron</p>
<p class="age">30</p>
</li>
...
</ol>
The code for searching is:
$("#search").change(function ()
{
var search = $(this).val();
var list = document.getElementById("people");
var items = list.children;
for (var i = 0; i < items.length; i++)
{
var item = items[i];
var name = item.getAttribute("data-name");
if(search == "")
{
item.style.display = "block";
}
else if(name.indexOf(search) != -1)
{
item.style.display = "block";
}
else
{
item.style.display = "none";
}
}
});
The code for sorting is:
$("#sort").click(function ()
{
var list = document.getElementById("people");
var items = list.children;
var itemArray = [];
for (var i = 0; i < items.length; i++)
{
itemArray.push(items[i]);
}
itemArray.sort(function (a, b)
{
var nameA = a.getAttribute("data-name");
var nameB = b.getAttribute("data-name");
return nameA.localeCompare(nameB);
});
for (var i = 0; i < itemArray.length; i++)
{
list.appendChild(itemArray[i]);
}
});
The code for swapping is:
$("#swap").click(function ()
{
var $people = $("#people");
$people.empty();
for(var i = 0; i < groupTwo.length; i++)
{
var person = groupTwo[i];
var container = document.createElement("li");
container.className = "person";
container.setAttribute("data-name", person.name);
container.setAttribute("data-nickname", person.nickname);
container.setAttribute("data-age", person.age);
var nameParagraph = document.createElement("p");
nameParagraph.className = "name";
nameParagraph.textContent = person.name;
var nicknameParagraph = document.createElement("p");
nicknameParagraph.className = "nickname";
nicknameParagraph.textContent = person.nickname;
var ageParagraph = document.createElement("p");
ageParagraph.className = "age";
ageParagraph.textContent = person.age;
container.appendChild(nameParagraph);
container.appendChild(nicknameParagraph);
container.appendChild(ageParagraph);
$people.append(container);
}
});
Discoveries so far:
Absolute Positioning really hurts smoothness of scrolling
Using nested divs for positioning really improves scrolling but hurts swapping via $.empty(), sorting via $.append(), and even causes crashes
Inline Block seems to be better for scrolling than float
Table Layout may improve scrolling but doesn't help with swapping/sorting
So I will start with my needs. I have a task to create json output using nightwatch.js from the ul list where inside lists are few div elements with classes like name, surname... But really I can't think of any of solutions. Here is my html
<html>
<meta charset="utf-8">
<body>
<ul class="random">
<li class="list">
<div class="name">John</div>
<div class="surname">Lewis</div>
</li>
<li class="list odd">
<div class="name">Nick</div>
<div class="surname">Kyrgios</div>
</li>
</ul>
</body>
</html>
And here is my nightwatch.js script
'Test' : function(browser) {
function iterate(elements) {
elements.value.forEach(function(el) {
browser.elementIdText(el.ELEMENT, function(r) {
browser.elementIdAttribute(el.ELEMENT, 'class', function(att){
// output for json i guess
console.log(att.value + ' => ' + r.value)
})
});
});
}
browser
.url('http://url.com/nightwatch.php')
.waitForElementVisible('body', 8000)
.elements('css selector', 'ul li div', iterate)
.end();
}
Basically this will execute the following:
name => John
surname => Lewis
name => Nick
surname => Kyrgios
Output is a string for both...
And how can I make it like
[{name: "John", surname: "Lewis"}, {name: "Nick", surname: "Kyrgios"}]
This should work. You just need to keep track of the object and place it inside the array after list.
function iterate(elements) {
var objArr = [];
var obj = {};
elements.value.forEach(function(el, idx) {
browser.elementIdText(el.ELEMENT, function(r) {
browser.elementIdAttribute(el.ELEMENT, 'class', function(att){
if (obj.hasOwnProperty(att.value)) {
objArr.push(obj);
obj = {};
}
obj[att.value] = r.value;
});
});
if (idx === (elements.value.length-1)) {
objArr.push(obj);
console.log(objArr);
}
});
}
As with Will's solution, I used straight JavaScript. It does not appear that the nightwatch.js code for this provides any significant benefit. In addition, your question does not specify that only nightwatch.js should be used.
As opposed to Will, I have assumed that the class on your inner <div> elements could be arbitrary and that the arbitrary class should be used as the key/property on the object for that entry. Choosing to use this method vs. restricting it only to a name or surname property will depend on what your HTML really is, and how you want to handle classes which are not those two strings.
var theList = [];
var listItems = document.querySelectorAll('li');
for (var itemIndex=0,itemLength=listItems.length; itemIndex < itemLength; itemIndex++) {
var entry = {};
divs = listItems[itemIndex].querySelectorAll('div');
for (var divsIndex=0, divsLength=divs.length; divsIndex < divsLength; divsIndex++) {
entry[divs[divsIndex].className] = divs[divsIndex].textContent;
}
theList.push(entry);
}
outputJson = JSON.stringify(theList);
console.log(outputJson);
<html>
<meta charset="utf-8">
<body>
<ul class="random">
<li class="list">
<div class="name">John</div>
<div class="surname">Lewis</div>
</li>
<li class="list odd">
<div class="name">Nick</div>
<div class="surname">Kyrgios</div>
</li>
</ul>
</body>
</html>
What about something like this?
function iterate(elements) {
var jsonArray = [];
var jsonBuffer = "";
elements.value.forEach(function(el) {
browser.elementIdText(el.ELEMENT, function(r) {
browser.elementIdAttribute(el.ELEMENT, 'class', function(att){
// output for json i guess
if (att.value == 'name') {
jsonBuffer += "{" + att.value + ":" + "" + r.value + "" + ",";
}
else {
jsonBuffer += att.value + ":" + "" + r.value + "" + "}";
jsonArray.push(jsonBuffer);
jsonBuffer = "";
}
})
});
});
var jsonOutput = "[";
var i = 0;
jsonArray.forEach(function(el) {
if (i < jsonArray.length) {
jsonOutput += el + ",";
} else {
jsonOutput += el + "]";
}
i++;
}
}
I'm not familiar with Nightwatch, but you essentially loop through the elements and push them on to an array.
var results = [];
var entries = document.querySelectorAll('li');
for (var ix = 0; ix < entries.length; ix++) {
var name = entries[ix].querySelector('.name').innerText;
var surname = entries[ix].querySelector('.surname').innerText;
results.push({
name: name,
surname: surname
});
}
console.log(results);
<ul class="random">
<li class="list">
<div class="name">John</div>
<div class="surname">Lewis</div>
</li>
<li class="list odd">
<div class="name">Nick</div>
<div class="surname">Kyrgios</div>
</li>
</ul>