I've created a Template Literal that displays information about a product. Open the collapse, and click a button to display a modal for additional information.
I'm passing strings of text to output to a ul within the modal. If a string is empty, it's displaying an output of "undefined".
If a string is empty, how do I not display that li at all?
// Javascript
const eventList = [
{
compShort: "competency-goes-here",
courseTitle: "Course Title is Longer and will Go Here",
courseComp: "Category Goes Here",
courseNumber: "course1",
courseDesc:
"Lorem ipsum dolor, sit amet consectetur adipisicing elit. Tempore id perspiciatis aliquam, error voluptatem consequuntur similique eligendi vitae odit veritatis iusto totam autem praesentium eum rerum alias, fugiat corporis quisquam iure suscipit beatae voluptate fugit tenetur itaque.",
coursePID: "123456789"
}
];
function eventTemplate(events) {
return `
<div class="col-sm-12">
<div class="row f-cat active" data-cat="${events.compShort}">
<div class="accordionCourses" id="accordionexample">
<div class="card border-1 border-dark-light">
<div class="row">
<div class="col-md-12">
<div class="card-body pb-0">
<h5 class="card-title font-weight-bold ism-text-dblue">${events.courseTitle}</h5>
<p class="card-text color-dark font-weight-bold text-italic">Competency Covered: ${events.courseComp}</p>
</div>
<div class="card-header bg-transparent border-0" id="headingone">
<h2 class="mb-0">
<button class="btn btn-link p-0 float-right" type="button" data-toggle="collapse" data-target="#${events.courseNumber}" aria-expanded="true" aria-controls="${events.courseNumber}">
<i class="fas fa-angle-double-down fa-w-10 fa-1x ism-text-green"><span hidden>text</span></i>
</button>
</h2>
</div>
</div>
</div>
</div>
<div id="${events.courseNumber}" class="collapse bg-grey" aria-labelledby="headingone" data-parent="#accordionexample">
<div class="card-body p-3">
<p class="card-text mb-3"> ${events.courseDesc} </p>
</div><!-- new row -->
<div class="card-footer border-0 bg-transparent pt-0">
<div class="row no-gutters">
<div class="col-sm-12 pb-3"><h6><span class="fas fa-plus-square"></span> View More</h6></div>
<div class="col-sm-12 pb-3"><h6><strong>Member: $135</strong> | Nonmember: $199</h6></div>
<div class="col-sm-12">Purchase</div>
</div>
</div>
</div>
</div>
</div>
</div>
`;
}
document.getElementById("eLearningCrsList").innerHTML = `
${eventList.map(eventTemplate).join(" ")}
`;
const eLmodalList = [
{
AriaId: 'course1',
learn1: 'Lorem ipsum dolor sit amet consectetur adipisicing elit. Quos aut id at fuga beatae, vel neque ea delectus? Ad, sequi? Animi perferendis rerum ad amet.',
learn2: 'Lorem ipsum dolor sit amet consectetur adipisicing elit. Ea quisquam non minima velit dicta praesentium, itaque ad saepe autem dignissimos porro quae eius accusantium. Cumque!',
learn3: 'Lorem ipsum, dolor sit amet consectetur adipisicing elit. Molestias magni explicabo ex, quibusdam quae illo quasi esse iusto! Quas, vero quibusdam. Voluptatem laborum modi architecto!'
}
];
function eLmodalTemplate(modals) {
return `
<div class="modal fade" id="${modals.AriaId}Modal" tabindex="-1" role="dialog" aria-labelledby="${modals.AriaId}Label" aria-hidden="true">
<div class="modal-dialog modal-dialog-centered modal-lg" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="${modals.AriaId}Label">Additional Information</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
</div>
<div class="modal-body">
<div class="row">
<div class="col-sm-12">
<div class="card border-0">
<div class="card-body bg-grey p-2">
<h5 class="card-title font-weight-bold text-uppercase text-center m-0">You will earn 1 credit for this course</h5>
</div>
</div>
</div>
<div class="col-sm-12">
<div class="card border-0">
<h4 class="card-header bg-transparent border-0 pl-0">You will learn...</h4>
<div class="card-body pl-0">
<ul>
<li>${modals.learn1}</li>
<li>${modals.learn2}</li>
<li>${modals.learn3}</li>
<li>${modals.learn4}</li>
<li>${modals.learn5}</li>
<li>${modals.learn6}</li>
</ul>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
`;
}
document.getElementById("eLearningModalList").innerHTML = `
${eLmodalList.map(eLmodalTemplate).join(" ")}
`;
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.9.0/css/all.min.css">
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css">
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/js/bootstrap.bundle.min.js"></script>
<div class="container p-5">
<div class="row">
<div id="eLearningCrsList"></div>
</div>
</div>
<div id="eLearningModalList"></div>
Write a condition for each that only shows the <li> if there's a value:
${modals.learn1 ? `<li>${modals.learn1}</li>` : ""}
let modals = {
learn1: "One",
learn2: "",
learn3: "Three",
learn4: null,
learn5: "Five"
};
let result = `
${modals.learn1 ? `<li>${modals.learn1}</li>` : ""}
${modals.learn2 ? `<li>${modals.learn2}</li>` : ""}
${modals.learn3 ? `<li>${modals.learn3}</li>` : ""}
${modals.learn4 ? `<li>${modals.learn4}</li>` : ""}
${modals.learn5 ? `<li>${modals.learn5}</li>` : ""}
${modals.learn6 ? `<li>${modals.learn6}</li>` : ""}
`;
document.body.innerHTML = result;
Alternatively, if those are your only modals, you could implement a loop instead:
${Object.values(modals).map(m => m ? `<li>${m}</li>` : '').join("")}
let modals = {
learn1: "One",
learn2: "",
learn3: "Three",
learn4: null,
learn5: "Five"
};
let result = `${Object.values(modals).map(m => m ? `<li>${m}</li>` : '').join("")}`;
document.body.innerHTML = result;
Related
Im making a movie review page, i use a button to change the qualification.
How can i get the name of the movie when i press the button?
<div class="container-movies" id="container-movies">
<div class="movie">
<span class="text1" id="title">Transformers</span>
<p class="text2">
Lorem ipsum dolor sit amet consectetur adipisicing elit. Labore soluta ullam officiis tempore sapiente, nesciunt veniam. Vitae explicabo labore soluta quis, omnis vero nulla, dignissimos necessitatibus repellat perferendis quisquam laboriosam.
</p>
<span class="text2">gen</span>
<div class="container-cal">
<span class="cal">4</span>
<button id="cal-btn">qualify</button>
</div>
</div>
<div class="movie">
<span class="text" id="title">movie title</span>
<p class="text2">
Lorem, ipsum dolor sit amet consectetur adipisicing elit. At, sint fugit numquam dicta aperiam neque aliquam expedita ipsum sapiente assumenda rerum temporibus fuga similique sed, perspiciatis qui ipsa nihil adipisci.
</p>
<span class="text2">gen</span>
<div class="container-cal">
<span class="cal">4</span>
<button id="cal-btn">qualify</button>
</div>
</div>
This only sends me the name of the first node:
let cal = document.getElementById("cal-btn");
cal.addEventListener('click', getcal);
You can grab the constent of the id="title" and use it in the getcal function
const movieTitle = document.getElementById("title").innerText
document.getElementById("cal-btn"); will only return one element because ids are unique, you should use a class instead.
For each button, add a listener on click. Then because of the structure, search for the closest parent movie to find the title inside.
for (let btn of document.getElementsByClassName("cal-btn")) {
btn.addEventListener("click", () => {
console.log(btn.closest(".movie").querySelector(".title").textContent)
})
}
<div class="movie">
<span class="title">Transformers</span>
<button class="cal-btn">vote</button>
</div>
<div class="movie">
<span class="title">Another film</span>
<button class="cal-btn">vote</button>
</div>
You cant use the same id over and over again, in this case you should use a class or an custom attribute select all buttons and loop through them and call your function
let calBtns = document.querySelectorAll(".cal-btn");
calBtns.forEach(btn => {
btn.addEventListener('click', ()=>{
alert(btn.closest('.movie').querySelector('.title').innerText)
});
})
<div class="container-movies" id="container-movies">
<div class="movie">
<span class="text1 title">Transformers</span>
<p class="text2">Lorem ipsum</p>
<span class="text2">gen</span>
<div class="container-cal">
<span class="cal">4</span>
<button class="cal-btn">qualify</button>
</div>
</div>
<hr>
<div class="movie">
<span class="text title">movie title</span>
<p class="text2">Lorem, ipsum</p>
<span class="text2">gen</span>
<div class="container-cal">
<span class="cal">4</span>
<button class="cal-btn">qualify</button>
</div>
</div>
I'm trying to make a blog thumbnail and blog content with blog content input.
I have blog input form and the thumbnail on the same page, here's the code for the page:
let blogs = []
let month = [
"Jan",
"Feb",
"Mar",
"Apr",
"May",
"Jun",
"Jul",
"Aug",
"Sept",
"Oct",
"Nov",
"Dec"
]
function addBlog(event) {
event.preventDefault()
let inputName = document.getElementById("inputProjectName").value
let inputContent = document.getElementById("inputDescription").value
let inputImage = document.getElementById("inputImage").files[0]
const projectDate = {
startDate: document.getElementById("inputStartDate").value,
endDate: document.getElementById("inputEndDate").value
}
inputImage = URL.createObjectURL(inputImage)
let cardIcons = {
html: document.querySelector('input[name="checkHtml"]').checked,
css: document.querySelector('input[name="checkCss"]').checked,
nodeJs: document.querySelector('input[name="checkNode"]').checked,
reactJs: document.querySelector('input[name="checkReact"]').checked
}
let blog = {
title: inputName,
date: projectDate,
content: inputContent,
icons: cardIcons,
image: inputImage
}
blogs.push(blog)
console.table(blogs)
renderCard()
}
function renderCard() {
let containerBlog = document.getElementById("contents")
containerBlog.innerHTML = '';
const objectBlogString = JSON.stringify(blogs);
for (let i = 0; i < blogs.length; i++) {
localStorage.setItem(`${blogs[i].title}`, objectBlogString);
containerBlog.innerHTML += `
<div id="contents" class="mp-card">
<!--MPC = My Project Card-->
<div class="mpc-img">
<img src="${blogs[i].image}" alt="">
</div>
<div class="mpc-title">
<a href="blog-detail.html" onclick="renderBlog(event)" id='${blogs[i].title} action="blog-detail.html"'>
<p>${blogs[i].title}</p>
</a>
</div>
<div class="mpc-duration">
<small>Durasi: 1 Bulan</small>
</div>
<div class="mpc-content">
${blogs[i].content}
</div>
<div class="mpc-icons">
${(blogs[i].icons.html === true) ? '<i class="fa-brands fa-html5"></i>' : ''}
${(blogs[i].icons.css === true) ? '<i class="fa-brands fa-css3-alt"></i>' : ''}
${(blogs[i].icons.nodeJs === true) ? '<i class="fa-brands fa-node-js"></i>' : ''}
${(blogs[i].icons.reactJs === true) ? '<i class="fa-brands fa-react"></i>' : ''}
</div>
<div class="mpc-mod">
<button>Edit</button>
<button>Delete</button>
</div>
</div>
`
}
}
function renderBlog(event) {
let keyName;
if (event) {
keyName = event.path[1].id;
const myProjectBody = document.getElementById('myProjectBody');
const data = JSON.parse(localStorage.getItem(keyName))
myProjectBody.innerHTML = '';
for (let i = 0; i < data.length; i++) {
myProjectBody.innerHTML += `
<div id="blog-detail">
<!--BD = Blog Detail-->
<div class="bd-title">
<p>${data[i].title}</p>
</div>
<!--IDC = Image, Duration, Categories-->
<div class="bd-idc">
<div class="idc-left">
<img src="${data[i].image}" alt="Blog Image">
</div>
<div class="idc-right">
<p>Duration</p>
<div class="bd-duration">
<div style="padding-left: 2px;">
<i class="fa-solid fa-calendar-days"></i>
<p>1 Jan 2022 - 1 Aug 2022</p>
</div>
<div>
<i class="fa-solid fa-clock"></i>
<p>8 Month</p>
</div>
</div>
<div class="bd-tech">
<p>Technologies</p>
<div class="tech-body">
${(data[i].icons.html === true) ? '<div class="tech-html"><i class="fa-brands fa-html5"></i><p>HTML</p></div>' : ''}
${(data[i].icons.css === true) ? '<div class="tech-css"><i class="fa-brands fa-css3-alt"></i><p>CSS</p></div>' : ''}
${(data[i].icons.nodeJs === true) ? '<div class="tech-node"><i class="fa-brands fa-node-js"></i><p>nodeJs</p></div>' : ''}
${(data[i].icons.reactJs === true) ? '<div class="tech-react"><i class="fa-brands fa-react"></i><p>reactJs</p></div>' : ''}
</div>
</div>
</div>
</div>
<div class="bd-content">
<p>
${data[i].content}
</p>
</div>
</div>
`
}
}
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>My Project</title>
<link rel="stylesheet" href="css/style.css">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.1.1/css/all.min.css">
</head>
<body>
<!-- NAVBAR -->
<nav>
<div class="nav-left">
<img src="assets/logo.png" alt="">
<ol>
<li>
Home
</li>
<li>
My Project
</li>
</ol>
</div>
<div class="nav-right">
Contact me
</div>
</nav>
<!--xNAVBARx-->
<div id="myProjectBody">
<!--MPI = My Project Input-->
<div class="mpi-title">
<p>ADD BLOG</p>
</div>
<!--Form-->
<div class="mpi-form-container">
<div class="mpi-form">
<!--MP = My Project Input-->
<form onsubmit="addBlog(event)" action="blog-detail.html">
<div class="mpi-name">
<label for="inputProjectName">Project Name</label>
<input type="text" id="inputProjectName">
</div>
<div class="mpi-date">
<div class="date-start">
<label for="inputStartDate">Start Date</label>
<input type="date" id="inputStartDate">
</div>
<div class="date-end">
<label for="inputEndDate">End Date</label>
<input type="date" id="inputEndDate">
</div>
</div>
<div class="mpi-desc">
<label for="inputDescription">Description</label>
<textarea name="inputDescription" id="inputDescription"></textarea>
</div>
<div class="mpi-check-cont">
<label for="checkTitle">Technologies</label>
<div class="mpi-check">
<div class="check-left">
<div>
<label for="checkHtml" class="check-label">HTML
<input type="checkbox" id="checkHtml" name="checkHtml"check>
<span class="checkmark"></span>
</label>
</div>
<div>
<label for="checkNode" class="check-label">Node Js
<input type="checkbox" id="checkNode" name="checkNode">
<span class="checkmark"></span>
</label>
</div>
</div>
<div class="check-right">
<div>
<label for="checkCss" class="check-label">CSS
<input type="checkbox" id="checkCss" name="checkCss">
<span class="checkmark"></span>
</label>
</div>
<div>
<label for="checkReact" class="check-label">React Js
<input type="checkbox" id="checkReact" name="checkReact">
<span class="checkmark"></span>
</label>
</div>
</div>
</div>
</div>
<div>
<label>Upload Image</label>
<div class="mpi-image">
<label for="inputImage">
<div class="mpi-choose">Choose</div>
<div class="mpi-attach"><i class="fa-solid fa-paperclip"></i></div>
</label>
<input type="file" id="inputImage" hidden />
</div>
</div>
<div class="mpi-submit">
<button type="submit">Submit</button>
</div>
</form>
</div>
</div>
<!--xFormx-->
<!--My Project-->
<div class="mp-container">
<div class="mp-title">
<p>MY PROJECT</p>
</div>
<div id="contents" class="mp-card-container">
<div class="mp-card">
<!--MPC = My Project Card-->
<div class="mpc-img">
<img src="assets/DWB.png" alt="">
</div>
<div class="mpc-title">
<a href="blog-detail.html">
<p>Dumbways Bootcamp 2022</p>
</a>
</div>
<div class="mpc-duration">
<small>Durasi: 3 Bulan</small>
</div>
<div class="mpc-content">
content
</div>
<div class="mpc-icons">
<i class="fa-brands fa-html5"></i>
<i class="fa-brands fa-css3-alt"></i>
<i class="fa-brands fa-node-js"></i>
<i class="fa-brands fa-react"></i>
</div>
<div class="mpc-mod">
<button>Edit</button>
<button>Delete</button>
</div>
</div>
</div>
</div>
<!--xMy Projectx-->
</div>
<script src="js/blog.js"></script>
</body>
</html>
and here's the blog detail page that will show all of the input value that I submit:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Blog detail</title>
<link rel="stylesheet" href="css/style.css">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.1.1/css/all.min.css">
</head>
<body>
<!-- NAVBAR -->
<nav>
<div class="nav-left">
<img src="assets/logo.png" alt="">
<ol>
<li>
Home
</li>
<li>
My Project
</li>
</ol>
</div>
<div class="nav-right">
Contact me
</div>
</nav>
<!--xNAVBARx-->
<!--Blog Detail-->
<div id="blog-detail">
<!--BD = Blog Detail-->
<div class="bd-title">
<p>Blog Title</p>
</div>
<!--IDC = Image, Duration, Categories-->
<div class="bd-idc">
<div class="idc-left">
<img src="assets/DWB.png" alt="Blog Image">
</div>
<div class="idc-right">
<p>Duration</p>
<div class="bd-duration">
<div style="padding-left: 2px;">
<i class="fa-solid fa-calendar-days"></i>
<p>10 May 2022 - 10 Jul 2022</p>
</div>
<div>
<i class="fa-solid fa-clock"></i>
<p>3 Month</p>
</div>
</div>
<div class="bd-tech">
<p>Technologies</p>
<div class="tech-body">
<div class="tech-html">
<i class="fa-brands fa-html5"></i>
<p>HTML</p>
</div>
<div class="tech-css">
<i class="fa-brands fa-css3-alt"></i>
<p>CSS</p>
</div>
<div class="tech-node">
<i class="fa-brands fa-node-js"></i>
<p>nodeJs</p>
</div>
<div class="tech-react">
<i class="fa-brands fa-react"></i>
<p>reactJs</p>
</div>
</div>
</div>
</div>
</div>
<div class="bd-content">
<p>
Lorem ipsum dolor sit amet consectetur adipisicing elit. Suscipit libero sapiente, minus atque a nesciunt ipsum? Illum sapiente odio provident maxime aut nihil voluptatem officia voluptatibus vel! Necessitatibus, ipsam esse. Lorem ipsum dolor sit amet
consectetur adipisicing elit. Suscipit libero sapiente, minus atque a nesciunt ipsum? Illum sapiente odio provident maxime aut nihil voluptatem officia voluptatibus vel! Necessitatibus, ipsam esse. Lorem ipsum dolor sit amet consectetur
adipisicing elit. Suscipit libero sapiente, minus atque a nesciunt ipsum? Illum sapiente odio provident maxime aut nihil voluptatem officia voluptatibus vel! Necessitatibus, ipsam esse. Lorem ipsum dolor sit amet consectetur adipisicing
elit. Suscipit libero sapiente, minus atque a nesciunt ipsum? Illum sapiente odio provident maxime aut nihil voluptatem officia voluptatibus vel! Necessitatibus, ipsam esse. Lorem ipsum dolor sit amet consectetur adipisicing elit. Suscipit
libero sapiente, minus atque a nesciunt ipsum? Illum sapiente odio provident maxime aut nihil voluptatem officia voluptatibus vel! Necessitatibus, ipsam esse. Lorem ipsum dolor sit amet consectetur adipisicing elit. Suscipit libero sapiente,
minus atque a nesciunt ipsum? Illum sapiente odio provident maxime aut nihil voluptatem officia voluptatibus vel! Necessitatibus, ipsam esse. Lorem ipsum dolor sit amet consectetur adipisicing elit. Suscipit libero sapiente, minus atque
a nesciunt ipsum? Illum sapiente odio provident maxime aut nihil voluptatem officia voluptatibus vel! Necessitatibus, ipsam esse.
</p>
</div>
</div>
<!--xBlog Detailx-->
<script src="js/blog-detail.js"></script>
</body>
</html>
I store all the value from input in localStorage with the key name ${blogs[i].title} in the card title anchor which came from the title input form, how can I access the key name with localStorage.getItem for the specific title that I input?
Thanks in advance.
You have a link to navigate in each blog item:
<a href="blog-detail.html" onclick="renderBlog(event)" id='${blogs[i].title} action="blog-detail.html"'>
<p>${blogs[i].title}</p>
</a>
onclick run your renderBlog function and also navigate to blog-detail.html. You can run the function and avoid the navigation executing event.preventDefault(). It's makes sense if you show list of blogs and selected blog in same page, which isn't the case.
So, you need do the navigation. An option to indicate in the page the item to render is add a parameter in the Url. Sonmething like:
<a href="blog-detail.html?${blogs[i].title}" action="blog-detail.html"'>
<p>${blogs[i].title}</p>
</a>
And when you load your blog-detail.html, use window.location.search (removing the ? at position 0) to get the title and, with the title, you can access and use your data from storage.
I have a list of Bootstrap 4 cards, displayed on the 12 columns grid.
I have made a filter by year and a search-box. As can be seen in the example below, the search expression is matched against the title of each card and the selected year is matched against the value of the data-year attribute.
class CardsFilteringComponent {
constructor() {
this.filterableCardsList = document.querySelector('.filterableItems');
this.items = this.filterableCardsList.querySelectorAll('.card-container');
this.searchBox = this.filterableCardsList.querySelector('#searchBoxInput');
this.searchBtn = this.filterableCardsList.querySelector('#searchBoxSubmitBtn');
this.selectBox = this.filterableCardsList.querySelector('#selectYear');
this.isHiddenClass = 'd-none';
}
handleSearchButtonClick(target) {
// call the filter function
//this.handleSelectChange(target);
const expression = this.searchBox.value.toLowerCase();
[...this.items].forEach(item => {
const cardTitle = item.querySelector('.title').textContent;
const showItem = cardTitle.toLowerCase().indexOf(expression) > -1;
const method = showItem ? 'remove' : 'add';
item.classList[method](this.isHiddenClass);
});
}
handleSelectChange(target) {
// call the search function
//this.handleSearchButtonClick(target);
const {value } = target;
[...this.items].forEach(item => {
const year = item.dataset.year;
const showItem = year === value || value === 'ALL';
const method = showItem ? 'remove' : 'add';
item.classList[method](this.isHiddenClass);
});
}
init() {
this.searchBtn.addEventListener('click', () => {
this.handleSearchButtonClick();
});
this.searchBox.addEventListener('keyup', e => {
if (e.keyCode === 13) {
this.handleSearchButtonClick();
}
});
this.selectBox.addEventListener('change', e => {
this.handleSelectChange(e.target);
});
}
}
const cardsFilteringComponent = new CardsFilteringComponent();
cardsFilteringComponent.init();
.cards-grid>[class*='col-'] {
display: flex;
flex-direction: column;
margin-bottom: 25px;
}
.cards-grid .card {
flex-grow: 1;
display: flex;
flex-direction: column;
background: #fff;
border-radius: 2px;
transition: all 0.3s ease-in-out;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.12), 0 1px 2px rgba(0, 0, 0, 0.11);
}
<link href="https://stackpath.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css" rel="stylesheet"/>
<link href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.0/css/bootstrap.min.css" rel="stylesheet"/>
<div class="container-fluid">
<div class="filterableItems my-4">
<div class="input-group mb-2">
<input type="text" class="form-control" placeholder="Search" id="searchBoxInput">
<div class="input-group-append">
<button class="btn btn-secondary" id="searchBoxSubmitBtn" type="button">
<i class="fa fa-search"></i>
</button>
</div>
</div>
<div class="form-group mb-2">
<select id="selectYear" class="form-control">
<option value="ALL" selected>All</option>
<option value="2019">2019</option>
<option value="2020">2020</option>
<option value="2021">2021</option>
</select>
</div>
<div class="row cards-grid">
<div class="card-container col-xs-12 col-md-4" data-year="2019">
<div class="card">
<div class="cardImage">
<img src="https://picsum.photos/1200/800/?gravity=north" alt="Lorem ipsum dolor" class="img-fluid">
</div>
<div class="cardContent p-3">
<h5 class="title">Lorem ipsum dolor</h5>
<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Repellat voluptates modi hic molestias quam doloremque. Accusantium odio blanditiis, amet placeat distinctio, quam magni, nobis perferendis error dicta perspiciatis quod delectus.</p>
</div>
<div class="mt-auto p-3 text-right text-muted small">Published on January 2<sup>nd</sup> 2019</div>
</div>
</div>
<div class="card-container col-xs-12 col-md-4" data-year="2020">
<div class="card">
<div class="cardImage">
<img src="https://picsum.photos/1200/800/?gravity=south" alt="In, quod adipisci" class="img-fluid">
</div>
<div class="cardContent p-3">
<h5 class="title">In, quod adipisci</h5>
<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Quia eaque qui ipsa quod facere autem voluptatem.</p>
</div>
<div class="mt-auto p-3 text-right text-muted small">Published on January 3<sup>rd</sup> 2020</div>
</div>
</div>
<div class="card-container col-xs-12 col-md-4" data-year="2021">
<div class="card">
<div class="cardImage">
<img src="https://picsum.photos/1200/800/?gravity=west" alt="Pariatur, sit, dolor" class="img-fluid">
</div>
<div class="cardContent p-3">
<h5 class="title">Pariatur, sit, dolor</h5>
<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Dolorum eaque repellat cumque pariatur dolores enim quibusdam nemo distinctio, dolore incidunt cupiditate ea excepturi est architecto amet tempore voluptatibus alias doloremque.</p>
</div>
<div class="mt-auto p-3 text-right text-muted small">Published on January 2<sup>nd</sup> 2021</div>
</div>
</div>
</div>
</div>
</div>
The problem
Besides making these two filters work on their own, I need to make them work together: if I do a search followed by a filtering, only the search results should be filtered by year.
For this purpose I call each method inside the other one, as can be seen in the commented out lines //this.handleSearchButtonClick(target) and //this.handleSelectChange(target).
This results in the error:
Uncaught RangeError: Maximum call stack size exceeded
See the error in this fiddle.
I am looking for an elegant, non-hack, solution.
Questions:
Where is my mistake?
What is the optimal, non-hack fix?
Your mistake is your you are calling one function that is calling the other function which in turns calls the original function and this goes on until Maximum call stack size exceeded error is raised.
What you could do instead is: save the values of both filters and apply them simultaneously.
handleSearchButtonClick(target) {
const expression = this.searchBox.value.toLowerCase();
this.filters.search = expression; // save the expression
this.applyFilter();
}
handleSelectChange(target) {
const {value} = target;
this.filters.year = value; // save the year
this.applyFilter();
}
applyFilter() {
// For each item
// Filter by title first
const cardTitle = ...;
const showItemForSearch = cardTitle.toLowerCase().indexOf(this.filters.search) > -1;
// Filter by year next
const year = ...;
const showItemForYear = year === this.filters.year || this.filters.year === 'ALL';
// make it visible if both condition is met
const showItem = showItemForSearch && showItemForYear;
...
});
}
Here is an updated fiddle: https://jsfiddle.net/9ra4y0cz/1/
class CardsFilteringComponent {
constructor() {
this.isHiddenClass = 'd-none';
this.filterableCardsList = document.querySelector('.filterableItems');
this.items = this.filterableCardsList.querySelectorAll('.card-container');
// saving the initial or default values of the filters
this.filters = {
search: '',
year: 'ALL'
};
// saving each item into an array
// makes it easier to manipulate the state of each item i.e. hidden or visible
this.state = [];
this.items.forEach(item => {
this.state.push({
node: item,
hidden: false // initially saving all items as visible, will modify this later
});
});
this.searchBox = this.filterableCardsList.querySelector('#searchBoxInput');
this.searchBtn = this.filterableCardsList.querySelector('#searchBoxSubmitBtn');
this.selectBox = this.filterableCardsList.querySelector('#selectYear');
this.render(); // initial rendering, all items are visible
}
render() {
// reading value from the state array and applying classname according to .hidden property
this.state.forEach(item => {
const method = item.hidden ? 'add' : 'remove';
item.node.classList[method](this.isHiddenClass);
});
}
applyFilter() {
this.state.forEach(item => {
// Filter by title first
const cardTitle = item.node.querySelector('.title').textContent;
const showItemForSearch = cardTitle.toLowerCase().indexOf(this.filters.search) > -1;
// Filter by year next
const year = item.node.dataset.year;
const showItemForYear = year === this.filters.year || this.filters.year === 'ALL';
// make it visible if both condition is met
// .hidden is the opposite of that
item.hidden = !(showItemForSearch && showItemForYear);
});
this.render(); // re-render since the state array may have changed
}
handleSearchButtonClick(target) {
const expression = this.searchBox.value.toLowerCase();
this.filters.search = expression; // save the expression
this.applyFilter();
}
handleSelectChange(target) {
const { value } = target;
this.filters.year = value; // save the year
this.applyFilter();
}
init() {
this.searchBtn.addEventListener('click', () => {
this.handleSearchButtonClick();
});
this.searchBox.addEventListener('keyup', e => {
if (e.keyCode === 13) {
this.handleSearchButtonClick();
}
});
this.selectBox.addEventListener('change', e => {
this.handleSelectChange(e.target);
});
}
}
const cardsFilteringComponent = new CardsFilteringComponent();
cardsFilteringComponent.init();
you just have a recursive infinitive loop
function a(){ b();}
function b(){ a();}
a(); // recurisive infinite loop
You can prevent this by adding boolean condition
function a(guard){ if(!guard) b(true);}
function b(guard){ if(!guard) a(true);}
a();
maximum callstack? you must have infinite loop or something like that- in your code its quite simple: you use function which use function which use the same function which called it 😛
in your case- handleSelectChange triggers handleSearchButtonClick (on second line of this function body), and what handleSearchButtonClick does? boom! on the second line of its body: handleSelectChange
so you've done something like A triggers B, which triggers A, which triggers B and so on- perpetuum mobile 😉
so maybe something in this manner (I didn't test that! Just pseudocode)?
handleSearchButtonClick(target, fromOtherHandler = false) {
if (!fromOtherHandler) this.handleSelectChange(target, true);
/*rest of the function body*/
}
handleSelectChange(target, fromOtherHandler = false) {
if (!fromOtherHandler) this.handleSearchButtonClick(target, true);
/*rest of the function body*/
}
also you must change in init
this.searchBtn.addEventListener('click', (e) => {
this.handleSearchButtonClick(e.target);
});
so its will work just from event (so once) but not from function itself (many times)
in theory of course ;)
Here you need to simplify the flow of your code. Whenever you combine Filters with Search, You think of the flow as
Step 1 : User "Enters Text"
Step 2 : User "Filters" on results
However, this should translate to,
Step 1 : When user clicks search button, inspect for any filter.
Step 2 : Combine Filter params into "query string" (Even if empty, pass in empty filter).
Step 3 : Hit API with "text + filter" in query.
Step 4 : When user updates filter, Go to Step 2.
This approach avoids short-circuiting the call by using an additional flag as suggested in some of the other responses here.
Here is an updated snippet which will help achieve this.
class CardsFilteringComponent {
constructor() {
this.filterableCardsList = document.querySelector('.filterableItems');
this.items = this.filterableCardsList.querySelectorAll('.card-container');
this.searchBox = this.filterableCardsList.querySelector('#searchBoxInput');
this.searchBtn = this.filterableCardsList.querySelector('#searchBoxSubmitBtn');
this.selectBox = this.filterableCardsList.querySelector('#selectYear');
this.isHiddenClass = 'd-none';
this.filter = {};
}
isItemFiltered(item) {
const filters = Object.entries(this.filter);
let isFiltered = true;
filters.forEach(([key, value]) => {
isFiltered = isFiltered && item.dataset[key] == value
});
return isFiltered;
}
handleSearchButtonClick(target) {
// call the filter function
//this.handleSelectChange(target);
const expression = this.searchBox.value.toLowerCase();
[...this.items].forEach(item => {
const cardTitle = item.querySelector('.title').textContent;
const showItem = cardTitle.toLowerCase().indexOf(expression) > -1 && this.isItemFiltered(item);
const method = showItem ? 'remove' : 'add';
item.classList[method](this.isHiddenClass);
});
}
handleSelectChange(target) {
// call the search function
//this.handleSearchButtonClick(target);
const {
value
} = target;
// [...this.items].forEach(item => {
// const year = item.dataset.year;
// const showItem = year === value || value === 'ALL';
// const method = showItem ? 'remove' : 'add';
// item.classList[method](this.isHiddenClass);
// });
if (value == 'ALL') {
delete this.filter['year'];
} else {
this.filter['year'] = value;
}
this.handleSearchButtonClick();
}
init() {
this.searchBtn.addEventListener('click', () => {
this.handleSearchButtonClick();
});
this.searchBox.addEventListener('keyup', e => {
if (e.keyCode === 13) {
this.handleSearchButtonClick();
}
});
this.selectBox.addEventListener('change', e => {
this.handleSelectChange(e.target);
});
}
}
const cardsFilteringComponent = new CardsFilteringComponent();
cardsFilteringComponent.init();
.cards-grid>[class*='col-'] {
display: flex;
flex-direction: column;
margin-bottom: 25px;
}
.cards-grid .card {
flex-grow: 1;
display: flex;
flex-direction: column;
background: #fff;
border-radius: 2px;
transition: all 0.3s ease-in-out;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.12), 0 1px 2px rgba(0, 0, 0, 0.11);
}
<link href="https://stackpath.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css" rel="stylesheet" />
<link href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.0/css/bootstrap.min.css" rel="stylesheet" />
<div class="container-fluid">
<div class="filterableItems my-4">
<div class="input-group mb-2">
<input type="text" class="form-control" placeholder="Search" id="searchBoxInput">
<div class="input-group-append">
<button class="btn btn-secondary" id="searchBoxSubmitBtn" type="button">
<i class="fa fa-search"></i>
</button>
</div>
</div>
<div class="form-group mb-2">
<select id="selectYear" class="form-control">
<option value="ALL" selected>All</option>
<option value="2019">2019</option>
<option value="2020">2020</option>
<option value="2021">2021</option>
</select>
</div>
<div class="row cards-grid">
<div class="card-container col-xs-12 col-md-4" data-year="2019">
<div class="card">
<div class="cardImage">
<img src="https://picsum.photos/1200/800/?gravity=north" alt="Lorem ipsum dolor" class="img-fluid">
</div>
<div class="cardContent p-3">
<h5 class="title">Lorem ipsum dolor</h5>
<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Repellat voluptates modi hic molestias quam doloremque. Accusantium odio blanditiis, amet placeat distinctio, quam magni, nobis perferendis error dicta perspiciatis quod delectus.</p>
</div>
<div class="mt-auto p-3 text-right text-muted small">Published on January 2<sup>nd</sup> 2019</div>
</div>
</div>
<div class="card-container col-xs-12 col-md-4" data-year="2020">
<div class="card">
<div class="cardImage">
<img src="https://picsum.photos/1200/800/?gravity=north" alt="Lorem ipsum dolor" class="img-fluid">
</div>
<div class="cardContent p-3">
<h5 class="title">Lorem jagz dolor</h5>
<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Repellat voluptates modi hic molestias quam doloremque. Accusantium odio blanditiis, amet placeat distinctio, quam magni, nobis perferendis error dicta perspiciatis quod delectus.</p>
</div>
<div class="mt-auto p-3 text-right text-muted small">Published on January 2<sup>nd</sup> 2020</div>
</div>
</div>
<div class="card-container col-xs-12 col-md-4" data-year="2020">
<div class="card">
<div class="cardImage">
<img src="https://picsum.photos/1200/800/?gravity=north" alt="Lorem ipsum dolor" class="img-fluid">
</div>
<div class="cardContent p-3">
<h5 class="title">Lorem jag dolor</h5>
<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Repellat voluptates modi hic molestias quam doloremque. Accusantium odio blanditiis, amet placeat distinctio, quam magni, nobis perferendis error dicta perspiciatis quod delectus.</p>
</div>
<div class="mt-auto p-3 text-right text-muted small">Published on January 2<sup>nd</sup> 2020</div>
</div>
</div>
<div class="card-container col-xs-12 col-md-4" data-year="2020">
<div class="card">
<div class="cardImage">
<img src="https://picsum.photos/1200/800/?gravity=south" alt="In, quod adipisci" class="img-fluid">
</div>
<div class="cardContent p-3">
<h5 class="title">In, quod adipisci</h5>
<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Quia eaque qui ipsa quod facere autem voluptatem.</p>
</div>
<div class="mt-auto p-3 text-right text-muted small">Published on January 3<sup>rd</sup> 2020</div>
</div>
</div>
<div class="card-container col-xs-12 col-md-4" data-year="2021">
<div class="card">
<div class="cardImage">
<img src="https://picsum.photos/1200/800/?gravity=west" alt="Pariatur, sit, dolor" class="img-fluid">
</div>
<div class="cardContent p-3">
<h5 class="title">Pariatur, sit, dolor</h5>
<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Dolorum eaque repellat cumque pariatur dolores enim quibusdam nemo distinctio, dolore incidunt cupiditate ea excepturi est architecto amet tempore voluptatibus alias doloremque.</p>
</div>
<div class="mt-auto p-3 text-right text-muted small">Published on January 2<sup>nd</sup> 2021</div>
</div>
</div>
</div>
<div class="card-container col-xs-12 col-md-4" data-year="2021">
<div class="card">
<div class="cardImage">
<img src="https://picsum.photos/1200/800/?gravity=west" alt="Pariatur, sit, dolor" class="img-fluid">
</div>
<div class="cardContent p-3">
<h5 class="title">Pariatur, Jagz, dolor</h5>
<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Dolorum eaque repellat cumque pariatur dolores enim quibusdam nemo distinctio, dolore incidunt cupiditate ea excepturi est architecto amet tempore voluptatibus alias doloremque.</p>
</div>
<div class="mt-auto p-3 text-right text-muted small">Published on January 2<sup>nd</sup> 2021</div>
</div>
</div>
</div>
</div>
</div>
This is a version to which I have added a message for when no results are returned from the search/filter:
class CardsFilteringComponent {
constructor() {
this.isHiddenClass = 'd-none';
this.noResults = document.querySelector('#noResults');
this.filterableCardsList = document.querySelector('.filterableItems');
this.items = this.filterableCardsList.querySelectorAll('.card-container');
this.filters = {
search: '',
year: 'ALL'
};
this.state = [];
this.items.forEach(item => {
this.state.push({
node: item,
hidden: false
});
});
this.searchBox = this.filterableCardsList.querySelector('#searchBoxInput');
this.searchBtn = this.filterableCardsList.querySelector('#searchBoxSubmitBtn');
this.selectBox = this.filterableCardsList.querySelector('#selectYear');
this.render();
}
render() {
const visibleItems = [];
this.state.forEach(item => {
const method = item.hidden ? 'add' : 'remove';
item.node.classList[method](this.isHiddenClass);
if(!item.hidden){
visibleItems.push(item);
}
});
if(!visibleItems.length){
this.noResults.classList.remove('d-none');
} else {
this.noResults.classList.add('d-none');
}
}
applyFilter() {
this.state.forEach(item => {
const cardTitle = item.node.querySelector('.title').textContent;
const showItemForSearch = cardTitle.toLowerCase().indexOf(this.filters.search) > -1;
const year = item.node.dataset.year;
const showItemForYear = year === this.filters.year || this.filters.year === 'ALL';
item.hidden = !(showItemForSearch && showItemForYear);
});
this.render();
}
handleSearchButtonClick(target) {
const expression = this.searchBox.value.toLowerCase();
this.filters.search = expression;
this.applyFilter();
}
handleSelectChange(target) {
const {value} = target;
this.filters.year = value;
this.applyFilter();
}
init() {
this.searchBtn.addEventListener('click', () => {
this.handleSearchButtonClick();
});
this.searchBox.addEventListener('keyup', e => {
if (e.keyCode === 13) {
this.handleSearchButtonClick();
}
});
this.selectBox.addEventListener('change', e => {
this.handleSelectChange(e.target);
});
}
}
const cardsFilteringComponent = new CardsFilteringComponent();
cardsFilteringComponent.init();
.cards-grid > [class*='col-'] {
display: flex;
flex-direction: column;
margin-bottom: 25px;
}
.cards-grid .card {
flex-grow: 1;
display: flex;
flex-direction: column;
background: #fff;
border-radius: 2px;
transition: all 0.3s ease-in-out;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.12), 0 1px 2px rgba(0, 0, 0, 0.11);
}
<link href="https://stackpath.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css" rel="stylesheet"/>
<link href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.0/css/bootstrap.min.css" rel="stylesheet"/>
<div class="container-fluid">
<div class="filterableItems my-4">
<div class="input-group mb-2">
<input type="text" class="form-control" placeholder="Search" id="searchBoxInput">
<div class="input-group-append">
<button class="btn btn-secondary" id="searchBoxSubmitBtn" type="button">
<i class="fa fa-search"></i>
</button>
</div>
</div>
<div class="form-group mb-2">
<select id="selectYear" class="form-control">
<option value="ALL" selected>All</option>
<option value="2019">2019</option>
<option value="2020">2020</option>
<option value="2021">2021</option>
</select>
</div>
<div class="row cards-grid">
<div class="card-container col-xs-12 col-md-4" data-year="2019">
<div class="card">
<div class="cardImage">
<img src="https://picsum.photos/1200/800/?gravity=north" alt="Lorem ipsum dolor" class="img-fluid">
</div>
<div class="cardContent p-3">
<h5 class="title">Lorem ipsum dolor</h5>
<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Repellat voluptates modi hic molestias quam doloremque. Accusantium odio blanditiis, amet placeat distinctio, quam magni, nobis perferendis error dicta perspiciatis quod delectus.</p>
</div>
<div class="mt-auto p-3 text-right text-muted small">Published on January 2<sup>nd</sup> 2019</div>
</div>
</div>
<div class="card-container col-xs-12 col-md-4" data-year="2020">
<div class="card">
<div class="cardImage">
<img src="https://picsum.photos/1200/800/?gravity=south" alt="In, quod adipisci" class="img-fluid">
</div>
<div class="cardContent p-3">
<h5 class="title">In, quod adipisci</h5>
<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Quia eaque qui ipsa quod facere autem voluptatem.</p>
</div>
<div class="mt-auto p-3 text-right text-muted small">Published on January 3<sup>rd</sup> 2020</div>
</div>
</div>
<div class="card-container col-xs-12 col-md-4" data-year="2021">
<div class="card">
<div class="cardImage">
<img src="https://picsum.photos/1200/800/?gravity=west" alt="Pariatur, sit, dolor" class="img-fluid">
</div>
<div class="cardContent p-3">
<h5 class="title">Pariatur, sit, dolor</h5>
<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Dolorum eaque repellat cumque pariatur dolores enim quibusdam nemo distinctio, dolore incidunt cupiditate ea excepturi est architecto amet tempore voluptatibus alias doloremque.</p>
</div>
<div class="mt-auto p-3 text-right text-muted small">Published on January 2<sup>nd</sup> 2021</div>
</div>
</div>
</div>
<div class="d-none p-2 text-center text-muted" id="noResults">No results</div>
</div>
</div>
I am trying to close a div by clicking a button with SlideUp function.
But when I click on button it opens as expected and closes other divs. But doesn't close it re-opens itself.
Here is my html code
<div class="row all-row">
<div class="col-lg-4">
<div class="main-acc">
<div class="brand-wrapper">
<img src="img.png" alt="">
<h5 class="p-3">Lorem</h5>
<div class="card-wrapper">
<div class="card border-0 text-center">
<h5>Ipsunlore</h5>
<p>
Lorem ipsum dolor, sit amet consectetur adipisicing elit. Eius unde est ut consequatur error amet eligendi vero aut assumenda.
</p>
Find
</div>
</div>
<a class="btn d-flex justify-content-between align-items-center opener" href="#">
<h5 class="m-0">Lorem</h5>
<i class="fa fa-chevron-up pl-3"></i>
</a>
</div>
</div>
</div>
<div class="col-lg-4">
<div class="main-acc">
<div class="brand-wrapper">
<img src="image.jpg" alt="">
<h5 class="p-3">Lorem</h5>
<div class="card-wrapper">
<div class="card border-0 text-center">
<h5>Mister: Ipsun</h5>
<p>
Lorem ipsum dolor, sit amet consectetur adipisicing elit. Eius unde est ut consequatur error amet eligendi vero aut assumenda.
</p>
From
</div>
</div>
<a class="btn d-flex justify-content-between align-items-center opener" href="#">
<h5 class="m-0">Lorem</h5>
<i class="fa fa-chevron-up pl-3"></i>
</a>
</div>
</div>
</div>
</div>
and here is the Jquery codes:
<script>
$(".opener").click(function(){
$(".all-row").find(".card-wrapper").slideUp();
$(this).closest(".card-wrapper").slideToggle();
})
</script>
You use .closest(), which finds the closest matching parent. But what you need is .prev() because the element you want to target is the previous sibling... Not it's parent.
Additionnaly, you should remove this targeted element form the "all" .card-wrapper collection, using .not()
$(".opener").click(function() {
// The card previous to the clicked .card-opener anchor
var this_card = $(this).prev(".card-wrapper")
// Make sure all cards closes EXCEPT this one
$(".all-row").find(".card-wrapper").not(this_card).slideUp();
// Toggle this one
this_card.slideToggle();
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<link href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css"/>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/js/bootstrap.min.js"></script>
<div class="row all-row">
<div class="col-lg-4">
<div class="main-acc">
<div class="brand-wrapper">
<img src="img.png" alt="">
<h5 class="p-3">Lorem</h5>
<div class="card-wrapper">
<div class="card border-0 text-center">
<h5>Ipsunlore</h5>
<p>
Lorem ipsum dolor, sit amet consectetur adipisicing elit. Eius unde est ut consequatur error amet eligendi vero aut assumenda.
</p>
Find
</div>
</div>
<a class="btn d-flex justify-content-between align-items-center opener" href="#">
<h5 class="m-0">Lorem</h5>
<i class="fa fa-chevron-up pl-3"></i>
</a>
</div>
</div>
</div>
<div class="col-lg-4">
<div class="main-acc">
<div class="brand-wrapper">
<img src="image.jpg" alt="">
<h5 class="p-3">Lorem</h5>
<div class="card-wrapper">
<div class="card border-0 text-center">
<h5>Mister: Ipsun</h5>
<p>
Lorem ipsum dolor, sit amet consectetur adipisicing elit. Eius unde est ut consequatur error amet eligendi vero aut assumenda.
</p>
From
</div>
</div>
<a class="btn d-flex justify-content-between align-items-center opener" href="#">
<h5 class="m-0">Lorem</h5>
<i class="fa fa-chevron-up pl-3"></i>
</a>
</div>
</div>
</div>
</div>
I have a div like this
<div id="news" class="col-sm-6 col-md-4 col-lg-4 pb-70">
<div class="post-prev-img">
<img src="images/blog/post-prev-1.jpg" alt="img">
</div>
<div class="post-prev-title">
<h3>TIME FOR MINIMALISM</h3>
</div>
<div class="post-prev-info">
JULE 10<span class="slash-divider">/</span>JOHN DOE
</div>
<div class="post-prev-text">
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Recusandae, nostrum, cumque culpa provident aliquam commodi assumenda laudantium magnam illo nostrum.
</div>
<div class="post-prev-more-cont clearfix">
<div class="post-prev-more left">
READ MORE
</div>
<div class="right" >
<span aria-hidden="true" class="icon_comment_alt"></span><span class="icon-count">21</span>
<span aria-hidden="true" class="icon_heart_alt"></span><span class="icon-count">53</span>
<a href="#" class="post-prev-count dropdown-toggle" data-toggle="dropdown" aria-expanded="false" >
<span aria-hidden="true" class="social_share"></span>
</a>
<ul class="social-menu dropdown-menu dropdown-menu-right" role="menu">
<li><span aria-hidden="true" class="social_facebook"></span>
</li>
<li><span aria-hidden="true" class="social_twitter"></span></li>
<li><span aria-hidden="true" class="social_dribbble"></span></li>
</ul>
</div>
</div>
</div>
I need this to be my "skeleton" for my other divs.
Now i want to make a for and append the content of my array inside of it and print every div inside an HTML page. How do i do that?
Thanks
You do this as follows,
make a template in your html and hide using css #template, make a div to show all the items that we are going to dynamically make #list
<div id="list">
</div>
<div id="template">
<div class="col-sm-6 col-md-4 col-lg-4 pb-70">
<div class="post-prev-img">
<img src="{{img}}" alt="img">
</div>
<div class="post-prev-title">
<h3>{{name}}</h3>
</div>
<div class="post-prev-info">
JULE 10<span class="slash-divider">/</span>JOHN DOE
</div>
<div class="post-prev-text">
{{info}}
</div>
<div class="post-prev-more-cont clearfix">
<div class="post-prev-more left">
READ MORE
</div>
<div class="right" >
<span aria-hidden="true" class="icon_comment_alt"></span><span class="icon-count">21</span>
<span aria-hidden="true" class="icon_heart_alt"></span><span class="icon-count">53</span>
<a href="#" class="post-prev-count dropdown-toggle" data-toggle="dropdown" aria-expanded="false" >
<span aria-hidden="true" class="social_share"></span>
</a>
<ul class="social-menu dropdown-menu dropdown-menu-right" role="menu">
<li><span aria-hidden="true" class="social_facebook"></span>
</li>
<li><span aria-hidden="true" class="social_twitter"></span></li>
<li><span aria-hidden="true" class="social_dribbble"></span></li>
</ul>
</div>
</div>
</div>
</div>
CSS to hide the template
#template {
display: none;
}
Then make an array of all your needed object data circle it with a loop and replace the data holders.
Then insert the new html into the dom
var assets= [
{
name: "Time for JS",
img: "http://via.placeholder.com/350x150",
link: "",
info:"Lorem ipsum dolor sit amet, consectetur adipisicing elit. Recusandae, nostrum, cumque culpa provident aliquam commodi assumenda laudantium magnam illo nostrum.",
},
{
name: "Time for CSS",
img: "http://via.placeholder.com/350x150",
link: "",
info:"ipsum dolor sit amet, consectetur adipisicing elit. Recusandae, nostrum, cumque culpa provident aliquam commodi assumenda laudantium magnam illo nostrum .consectetur adipisicing",
},
{
name: "Time for HTML",
img: "http://via.placeholder.com/350x150",
link: "",
info:"consectetur adipisicing consectetur adipisicing elit. Recusandae, nostrum, cumque culpa provident aliquam commodi assumenda laudantium magnam illo nostrum .consectetur adipisicing",
}
];
var html=""
assets.forEach(buildTemplate);
document.getElementById("list").innerHTML=html;
function buildTemplate(item) {
var template = document.getElementById("template").innerHTML;
template = template.replace("{{name}}", item.name);
template = template.replace("{{info}}", item.info);
template = template.replace("{{img}}", item.img);
html+=template;
}
Here it is in action, it's that simple no jquery ultra fast hardcode JS.
https://jsfiddle.net/5yeh38LL/
There is a much easier way use a js template engine there are lots on google but this demonstrates how this is done and how effectively a template system will work.
Try jQuery templates. You write your HTML code inside script tags and in your JS code you can call them and use them as you wish:
<script id="bookTemplate" type="text/x-jQuery-tmpl">
<div>
<img src="BookPictures/${picture}" alt="" />
<h2>${title}</h2>
price: ${formatPrice(price)}
</div>
</script>
Usage:
$("#bookTemplate").tmpl(books).appendTo("#bookContainer");