How to set a display toggle in an array with pure javascript - javascript

I want to personalize a CMS for FAQ.
I need to display the 4 first questions and set a button to toggle the display of the other questions.
I'm using that function to display the questions :
function toggleViewMoreLess() {
let articlesRequestPromise = fetch("/api/v2/help_center/fr-fr/articles.json");
let articlesArray = [];
let arrayOfSectionLinks = document.getElementsByClassName('article-list');
articlesRequestPromise.then(response => {
return response.json();
}).then(results => {
articlesArray = results.articles;
for (let i = 0; i < arrayOfSectionLinks.length; i++) {
let aId = JSON.parse(arrayOfSectionLinks[i].id);
let threeArticles = articlesArray.filter(article => article.section_id === aId).slice(0, 3);
threeArticles.map(function (article) {
let articleLink = document.createElement("a");
articleLink.type = 'link';
articleLink.innerText = article.title;
articleLink.className = 'article-list-link';
articleLink.href = article.html_url + "_" + article.section_id;
return (
arrayOfSectionLinks[i].append(articleLink)
)
});
}
})
}
toggleViewMoreLess();
The idea I have and I can't achieve is to set a display none on the each section's array after index 3 and to set a ternary condition on the button for the toggle.
I'm more use to work with react than vanilla js so I'm kind of lost.
Ideally, I would like to do something like with useState

If your array is an array of HTML elements have you considered just using CSS for this? e.g. if you have an array of p tags:
p:nth-child( n+4 ).no-display {
display: none;
}
Then conditionally apply the class "no-display" to your tags

Related

How do I save code using loops while accessing json data?

I recently started my first programming project.
I started noticing that I use the same code over and over again, and would like to save that.
Here is an example:
document.getElementById('Serie1').textContent = showtrend.tv_results[0].title ;
document.getElementById('Serie1ID').textContent = "https://www.imdb.com/title/"+showtrend.tv_results[0].imdb_id ;
document.getElementById('Serie1Year').textContent = showtrend.tv_results[0].year ;
document.getElementById('Serie2').textContent = showtrend.tv_results[1].title ;
document.getElementById('Serie2ID').textContent = "https://www.imdb.com/title/"+showtrend.tv_results[1].imdb_id ;
document.getElementById('Serie2Year').textContent = showtrend.tv_results[1].year ;
I am basically adding the values I get in form of a json from my api to my site.
But how can I put all of this in a loop? It is like that for another 10 series, ant isn't very elegant
Would really appreciate the help
I think this might help you.
for (let i = 1; i < showtrend.tv_results.length; i++) {
var name = 'Serie'+ i;
document.getElementById(name).textContent = showtrend.tv_results[i-1].title ;
document.getElementById(name+'ID').textContent = "https://www.imdb.com/title/"+showtrend.tv_results[i-1].imdb_id ;
document.getElementById(name+'Year').textContent = showtrend.tv_results[i-1].year ;
}
How I would do:
ECMAScript
const BASE_URL = "https://www.imdb.com/title/";
showtrend.tv_results.forEach(function (result, i) {
let count = i + 1;
document.getElementById(`Serie${count}`).textContent = result.title;
document.getElementById(`Serie${count}ID`).textContent = BASE_URL + result.imdb_id;
document.getElementById(`Serie${count}Year`).textContent = result.year;
});
Vanilla JS
const BASE_URL = "https://www.imdb.com/title/";
for(var i = 1; i <= showtrend.tv_results.length; i++) {
document.getElementById('Serie' + i).textContent = result.title;
document.getElementById('Serie' + i + 'ID').textContent = BASE_URL + result.imdb_id;
document.getElementById('Serie' + i + 'Year').textContent = result.year;
});
In addition, maybe as your next challenge, try to get rid of these hard-coded elements in the list of "series" and make it dynamically created by pushing elements to an array in Javascript and populating a table or a list reading from this array. This will keep your code even more elegant.
Good luck with your studies!
Instead of having predefined elements in your page that you have to fill, have one container element, and then iterate over the tv_results array and compile the information from each object into divs, or a table, and then insert that HTML as the innerHTML of the container.
This method will allow you to have as many movies in the data as you need.
const json = '{"showtrend": {"tv_results": [{ "title": "Batman", "imdb_id": 1 },{ "title": "Ratman", "imdb_id": 2 }]}}';
const data = JSON.parse(json);
// Pass in the parsed data
function getHTML(data) {
// `map` over the tv_results array
return data.showtrend.tv_results.map(obj => {
// For each object in the iteration return a string of HTML.
// This method uses a template literal, and adds
// a data attribute to the outer div to identify the movie.
return (
`<div data-id="${obj.imdb_id}" class="movie">
<div>${obj.title}</div>
<div>${obj.imdb_id}</div>
</div>`
);
// Finally join the array that `map` returns
// and return that string
}).join('');
}
// Cache the container element
const container = document.querySelector('#container');
// Call `getHTML` and add the returned HTML string
// to the `innerHTML` of the container
container.innerHTML = getHTML(data);
.movie { margin-bottom: 0.5em; background-color: #efefef; padding: 0.2em; }
<div id="container"></div>
Additional documentation
querySelector
Template/string literals
map
join
Data attributes

JavaScript append multiple in for loop

Does anyone know what I'm doing wrong with the append?
function filterPosts(){
let filterValue = document.getElementById('search-filter').value.toUpperCase();
let posts = document.getElementById('posts');
let post = posts.querySelectorAll('div.post');
for (let i = 0; i < post.length; i++) {
let filterItem = post[i].getElementsByTagName('h5')[0];
if (filterItem.innerHTML.toUpperCase().indexOf(filterValue) > -1) {
post[i].append();
} else {
post[i].remove();
}
}
}
I've tried a few different things to no avail. I'm trying to remove elements based on type and then readd them if they exist based on heading.
for (let i = 0; i < post.length; i++) {
let filterItem = post[i].getElementsByTagName('h5')[0];
if (filterItem.innerHTML.toUpperCase().indexOf(filterValue) > -1) {
posts.appendChild(post[i]);
} else {
post[i].remove();
}
}
This ended up working for the filtering, but it doesnt bring back posts if textbox is backspaced or empty.
you could use https://developer.mozilla.org/en-US/docs/Web/API/Node/appendChild (has better browser support than append) and https://developer.mozilla.org/en-US/docs/Web/API/Node/removeChild - in your case this would looks like posts.appendChild(post[i]) and posts.removeChild(post[i])
you could also just change the visibility of the elements - this should be faster and simpler for your case.
another way would be to have an array with the all the posts data and work with it primarily. this way you can also "bring back the data".
each time the filter changes: create a new array by filtering the posts data, remove all post elements, then append only those that match the filtered data e.g.
let postsData = ["content1", "content2"];
for (let i = 0; i < post.length; i++) {
post[i].remove();
}
postsData.filter(function (post) {
return post.toUpperCase().indexOf(filterValue) > -1;
}).forEach(function () {
var postElem = document.createElement("div");
posts.appendChild(postElem);
});

how to create more than one element in the DOM with js using a for loop?

Well, the result I want to get is my div element should have the same numbers of p elements as the array.
In order to do this, I tried using a for loop but it just create one p element, so I didn't work.
Anyone have idea how to do this? (I'm just learning js).
const diccionario = () => {
var dama = ["bird:pajaro", "car:carro", "house:csa", "camis"];
document.getElementById("dic").addEventListener("click", () => {
var pes = document.createElement("p");
for(var i = 0; i < dama.length; i++){
pes.innerHTML = dama[i];
document.getElementById("cuadro").appendChild(pes);
}
})
}
You need to create the element inside the loop. If you create it outside the loop, then when you call appendChild, it will get removed from wherever it was previously in the DOM:
const diccionario = () => {
var dama = ["bird:pajaro", "car:carro", "house:csa", "camis"];
document.getElementById("dic").addEventListener("click", () => {
for(var i = 0; i < dama.length; i++){
var pes = document.createElement("p"); // <----------------
pes.innerHTML = dama[i];
document.getElementById("cuadro").appendChild(pes);
}
})
}
appendChild doesn't create a copy of the existing element - rather, it just appends the existing element.

How to make a search box that searches for attributes' value then hide or show elements based on users input?

I'm pretty new to Javascript. I'm trying to make a search box that searches for images based users input. Here's what I got so far.
<input type="text" id="search-box">
<a class="image-link" href="photos/02.jpg" data-rel="lightcase:collection" data-lc-caption="The lake was so calm today. We had a great view of the snow on the mountains from here." data-alt="Lake">
<img class="image-link" src="photos/thumbnails/02.jpg" alt="Lake">
</a>
So, I'm trying to make a search that uses attributes value to display/hide the images. For example, if I typed "lake" in the search box this image and any images that have the word "lake" as their attributes' value would appear and hid the rest.
Here's my Javascript code so far. I'm not quite sure if I'm on the right path here. I'd appreciate any help, please.
let y = document.getElementsByTagName('a');
document.getElementById("search-box").addEventListener("keyup", function() {
x = document.getElementById("search-box");
x = x.value.toLowerCase();
console.log(x);
for (i = 0; i < y.length; i += 1) {
e = descent[i].getAttribute("data-lc-caption").toLowerCase();
console.log(e);
}
if (e.includes(x)) {
console.log(yes);
}
});
I used console.logs to check my results. I know that I'll have to remove them eventually.
You are on right path. Here are few general suggestions based on your code
1 Use better variable names, for example instead y name it something
like videoLinks
2 Use const unless you know you will need to change value of variable at some point
3 Do not declare variable by just saying x = 'something'. That way you are creating global variables and using memory for no reason
4 Use onchange input because onkeyup will fire only on typing, what is user pastes in content?
Now the code:
document.getElementById('search-box').addEventListener('change', function() {
const linksWithImages = document.getElementsByTagName('a')
const searchValue = document.getElementById("search-box").value
for (let i = 0; i < linksWithImages.length) {
if (linksWithImages[i].getAttribute('data-lc-caption') === searchValue) {
linksWithImages[i].style.display === 'block'
} else {
linksWithImages[i].style.display === 'none'
}
}
})
Firstly, let's improve the event listener by detecting change and call the main function only after the user finish typing. Then use RegExp instead of toLowerCase for Case-insensitive search. And finally, update the style.
Also, it's better to get the images by using class name instead of tag name.
const searchBox = document.getElementById("search-box");
const images = document.getElementsByTagName("a");
let globalTimeout = null;
// Detect for input change when user finish typing
searchBox.addEventListener("keyup", function () {
if (globalTimeout !== null)
clearTimeout(globalTimeout);
globalTimeout = setTimeout(onFinishTyping, 200);
});
function onFinishTyping() {
const input = searchBox.value;
globalTimeout = null;
console.log("Searching for", input);
for (let i = 0, length = images.length; i < length; i++) {
const image = images[i];
if (!image.getAttribute("data-lc-caption").match(new RegExp(input, "ig")))
image.style.display = "none";
else
image.style.display = "block";
}
}
document.getElementById('search-box').addEventListener('input', function() {
const linksWithImages = document.getElementsByTagName('a')
const searchValue = document.getElementById("search-box").value
let i = 0, len = linksWithImages.length;
for (; i < len; i++) {
let lnk = linksWithImages[i];
let val = lnk.getAttribute("data-lc-caption");
lnk.style.visibility = val.indexOf(searchValue) > -1 ? "visible" : "hidden";
}
})

pure javascript, filter box doesn't filter properly

I have an array of devices, each device has own unique id, I wish to make searchBox which will filter by this id. So far I managed it partly, so it checks if character from input match character from device-id. However not in way I would want that, example below:
id =35678; input.value = 5
it shouldn't work as first character is 3
id = 35679; input.value= 35
it should work as first character is same
Problem is in match/include function, but no really idea how to replace it to make it work
input_box.addEventListener('keyup', function(){
var search_result = this.value;
var device = document.querySelectorAll('[device_id]')
var array_of_devices = [];
for (var i = 0; i < device.length; i++) {
array_of_devices.push(device[i].getAttribute('device_id'))
}
array_of_devices.forEach(el => {
if (!el.match(search_result)) {
var not_matched = document.querySelector(`[device_id="${el}"]`)
not_matched.style.display = "none"
} else {
var matched = document.querySelector(`[device_id="${el}"]`)
matched.style.display = "block"
}
})
})
You can use startsWith instead of match
let arr = ['35678', '451234', '45454', '56565']
let find = (value) =>{
return arr.filter(id=> id.startsWith(value))
}
console.log(find(5))
console.log(find(35))
console.log(find(46))
Instead of using .match you can simply use .indexOf and check the index, if it 0 then the entered string is matching the device name from the begining.
array_of_devices.forEach(el => {
// Condition below is changed to use indexOf
if (el.indexOf(search_result) === 0) {
var not_matched = document.querySelector(`[device_id="${el}"]`)
not_matched.style.display = "none"
} else {
var matched = document.querySelector(`[device_id="${el}"]`)
matched.style.display = "block"
}
});
I would suggest you to build a string of device elements based on the search string and add it to your DOM instead of modifying the display properties. This is costing you a bunch of DOM operations which is computationally heavy.
Notice each id in array should be string
const ids = ['3575', '5555']
const found = value => ids.filter(i => i.indexOf(value, 0) === 0)
console.log(found(5));
console.log(found(35));

Categories