'is there a way to prevent repeating code in html I'm using bootstrap 4 collapse like 20 times in one page
<div class="card" style>
<div class="card-header" id="headingOne">
<h5 class="mb-0">
<button class="btn btn-link" data-toggle="collapse" data-target="#collapseOne" aria-
expanded="true" aria-controls="collapseOne">
Collapsible Group Item #1
</button>
</h5>
</div>
<!-- Add mx-auto -->
<div id="collapseOne" class="collapse show text-center mx-auto" aria-labelledby="headingOne"
style="width:300px;">
<div class="card-body">
<div class="card">
<div class="card-header text-center" id="headingTwo" style="width:300px;">
<h5 class="mb-0">
<button class="btn btn-link collapsed text-center" data- toggle="collapse" data-
target="#collapseTwo" aria-expanded="false" aria-controls="collapseTwo" style="width:300px;">
Collapsible Group Item #2
</button>
</h5>
</div>
<div id="collapseTwo" class="collapse show" aria-labelledby="headingTwo">
<div class="card-body">
Anim pariatur cliche reprehenderit, enim eiusmod high life accusamus wolf moon put a
craft beer sapiente ea proident. Ad vegan excepteur butcher vice lomo. Leggings farm-
</div>
</div>
</div>
</div>
</div>
</div>
<div class="card">
<div class="card-header" id="headingThree">
<h5 class="mb-0">
<button class="btn btn-link collapsed" data-toggle="collapse" data- target="#collapseThree"
aria-expanded="false" aria-controls="collapseThree">
Collapsible Group Item #3
</button>
</h5>
</div>
<div id="collapseThree" class="collapse" aria-labelledby="headingThree">
<div class="card-body">
Anim pariatur cliche reprehenderit, enim eiusmod high life accusamus terry wolf moon
et. Nihil anim keffiyeh helvetica, craft beer labore wes sapiente ea proident. Ad vegan
</div>
</div>
</div>
</div>
Hello I have been copying and pasting this like 10-20 times on my code changing the id the header id and the button. I also had been copying and pasting a bunch of other html code (image tag about 20 images) I was wondering if there was a way to stop repeating code (like a function) with pure html css. If not can you do it with plain vanilla js? if not then what are the other ways?
In short
Most important question (A is most important C is least)
- A) is there a way to prevent repeating code with the collapse code above with just pure HTML/CSS? if yes how?
- B) is there a way to prevent repeating code with the collapse code above with just pure HTML/CSS/vanilla js? if yes how?
- C) is there a way to prevent repeating code with the collapse code above with just pure HTML/CSS/angular/react? if yes how?
Solution 1:[1]
You can use a function to repeat how many times you want to duplicate your element, in the following snippet I created a function that will clone .card
node using .cloneNode()
and iterate a number of times, then modify the ids and attributes using querySelector()
on the cloned node, here is a snippet for option A
:
function duplicateElement(selector, numOfDuplicates) {
let element = document.querySelector(selector);
for(let i = numOfDuplicates; i > 0; i--) {
let newElement = element.cloneNode(true);
newElement.querySelector('#headingOne').id = `headingOne-${i}`;
newElement.querySelector('#collapseOne').id = `collapseOne-${i}`;
newElement.querySelector('#collapseTwo').id = `collapseTwo-${i}`;
newElement.querySelector('#headingTwo').id = `headingTwo-${i}`;
let toggleBtn = newElement.querySelector('[data-target="#collapseOne"]');
toggleBtn.dataset['target'] = `#collapseOne-${i}`;
toggleBtn.setAttribute('aria-controls', `collapseOne-${i}`);
let toggleBtn2 = newElement.querySelector('[data-target="#collapseTwo"]');
toggleBtn2.dataset['target'] = `#collapseTwo-${i}`;
toggleBtn2.setAttribute('aria-controls', `collapseTwo-${i}`);
element.after(newElement);
}
}
duplicateElement('.card', 4);
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css" integrity="sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm" crossorigin="anonymous">
<script src="https://code.jquery.com/jquery-3.2.1.slim.min.js" integrity="sha384-KJ3o2DKtIkvYIK3UENzmM7KCkRr/rE9/Qpg6aAZGJwFDMVNA/GpGFF93hXpG5KkN" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.12.9/umd/popper.min.js" integrity="sha384-ApNbgh9B+Y1QKtv3Rn7W3mgPxhU9K/ScQsAP7hUibX39j7fakFPskvXusvfa0b4Q" crossorigin="anonymous"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/js/bootstrap.min.js" integrity="sha384-JZR6Spejh4U02d8jOt6vLEHfe/JQGiRRSQQxSfFWpi1MquVdAyjUar5+76PVCmYl" crossorigin="anonymous"></script>
<div class="card">
<div class="card-header" id="headingOne">
<h5 class="mb-0">
<button class="btn btn-link" data-toggle="collapse" data-target="#collapseOne" aria- expanded="true" aria-controls="collapseOne">
Collapsible Group Item #1
</button>
</h5>
</div>
<!-- Add mx-auto -->
<div id="collapseOne" class="collapse show text-center mx-auto" aria-labelledby="headingOne" style="width: 300px;">
<div class="card-body">
<div class="card">
<div class="card-header text-center" id="headingTwo" style="width: 300px;">
<h5 class="mb-0">
<button class="btn btn-link collapsed text-center" data-toggle="collapse" data-target="#collapseTwo" aria-expanded="false" aria-controls="collapseTwo" style="width: 300px;">
Collapsible Group Item #2
</button>
</h5>
</div>
<div id="collapseTwo" class="collapse show" aria-labelledby="headingTwo">
<div class="card-body">
Anim pariatur cliche reprehenderit, enim eiusmod high life accusamus wolf moon put a craft beer sapiente ea proident. Ad vegan excepteur butcher vice lomo. Leggings farm-
</div>
</div>
</div>
</div>
</div>
</div>
If you are looking for option C
solution, it will be a bit complicated if your project is not already using React or other client rendering library or front-end framework, you have to use Webpack with Babel to setup the front-end framework and code-base for your project, here is a working codesandbox sample for on how you might implement your bootstrap front-end using React.
In the following snippet, I created a component function and used Array.map()
on array [1, 2, 3, 4, 5]
to create 5 components:
import React, { useState } from "react";
import { Collapse, Button, Card } from "react-bootstrap";
import "bootstrap/dist/css/bootstrap.min.css";
import "./styles.css";
function Example() {
const [open, setOpen] = useState(false);
const [openLevel, setOpenLevel] = useState(false);
return (
<>
<Card>
<Card.Header>
<Button
onClick={() => setOpen(!open)}
aria-controls="collapseOne"
aria-expanded={open}
>
Collapsible Group Item #1
</Button>
</Card.Header>
<Collapse className="first-coll" in={open}>
<Card.Body id="collapseOne">
<Card>
<Card.Header>
<Button
onClick={() => setOpenLevel(!openLevel)}
aria-controls="collapseTwo"
aria-expanded={openLevel}
>
Collapsible Group Item #2
</Button>
</Card.Header>
<Collapse className="second-coll" in={openLevel}>
<Card.Body id="collapseTwo">
Anim pariatur cliche reprehenderit, enim eiusmod high life
accusamus wolf moon put a craft beer sapiente ea proident. Ad
vegan excepteur butcher vice lomo. Leggings farm-
</Card.Body>
</Collapse>
</Card>
</Card.Body>
</Collapse>
</Card>
</>
);
}
export default function App() {
return (
<div className="App">
{[1, 2, 3, 4, 5].map((el, i) => {
return <Example key={i} />;
})}
</div>
);
}
Solution 2:[2]
In pure HTML/CSS this isn't possible, unless you employ a server-side rendering with some template language.
As far as doing it with pure HTML/CSS/JS, ECMAScript 262 introduced template literals which can be employed to great effectiveness here:
<script>
function createCard(id, content) {
return `<div class="card">
<div class="card-header" id="heading-${id}">
<h5 class="mb-0">
<button class="btn btn-link collapsed" data-toggle="collapse" data-target="#collapse-${id}" aria-expanded="false" aria-controls="collapse-${id}">Collapsible Group Item #${id}</button>
</h5>
</div>
<div id="collapse-${id}" class="collapse" aria-labelledby="heading-${id}">
<div class="card-body">
${content}
</div>
</div>
</div>`;
}
$(function(){
// create one card
$('#cards').append($(createCard('1', "Lorem ipsum")));
// create nested cards
$('#cards').append($(createCard('2', createCard(3, "Dolor sit amnet"))));
});
</script>
Note that I have employed jQuery here for the ease of selecting the element & inserting and because Bootstrap -- which you are using -- uses jQuery anyways. You can easily do this with pure JS via document.getElementById(...)
as well.
A working demo can be seen here:
<!doctype html>
<html lang="en">
<head>
<!-- Required meta tags -->
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<!-- Bootstrap CSS -->
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.0/css/bootstrap.min.css" integrity="sha384-9aIt2nRpC12Uk9gS9baDl411NQApFmC26EwAOH8WgZl5MYYxFfc+NcPb1dKGj7Sk" crossorigin="anonymous">
<title>Hello, world!</title>
</head>
<body>
<h1>Hello, world!</h1>
<div class="container" id="cards">
</div>
<!-- Optional JavaScript -->
<!-- jQuery first, then Popper.js, then Bootstrap JS -->
<script src="https://code.jquery.com/jquery-3.5.1.slim.min.js" integrity="sha384-DfXdz2htPH0lsSSs5nCTpuj/zy4C+OGpamoFVy38MVBnE+IbbVYUew+OrCXaRkfj" crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/umd/popper.min.js" integrity="sha384-Q6E9RHvbIyZFJoft+2mJbHaEWldlvI9IOYy5n3zV9zzTtmI3UksdQRVvoxMfooAo" crossorigin="anonymous"></script>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.5.0/js/bootstrap.min.js" integrity="sha384-OgVRvuATP1z7JjHLkuOU7Xw704+h835Lr+6QL9UvYjZE3Ipu6Tp75j7Bh/kR0JKI" crossorigin="anonymous"></script>
<script>
function createCard(id, content) {
return `<div class="card">
<div class="card-header" id="heading-${id}">
<h5 class="mb-0">
<button class="btn btn-link collapsed" data-toggle="collapse" data-target="#collapse-${id}" aria-expanded="false" aria-controls="collapse-${id}">Collapsible Group Item #${id}</button>
</h5>
</div>
<div id="collapse-${id}" class="collapse" aria-labelledby="heading-${id}">
<div class="card-body">
${content}
</div>
</div>
</div>`;
}
$(function(){
$('#cards').append($(createCard('1', "Lorem ipsum")));
$('#cards').append($(createCard('2', createCard(3, "Dolor sit amnet"))));
});
</script>
</body>
</html>
Solution 3:[3]
I reckon I have found another answer, it involves the html <template>
tag and json to get the data.
html template
The HTML Content Template (
<template>
) element is a mechanism for holding HTML that is not to be rendered immediately when a page is loaded but may be instantiated subsequently during runtime using JavaScript.
Thus it is exactly what you need for this kind of problem.
Solution
First of all, you need to specify the button text and the card-content in a json object. This json can reside in a separate file (and loaded as seen below) or you can place the json content inside a variable as seen in the jsfiddle.
1. Using a Json file
data.json
[
{
"button" : "Button 1",
"body" : "Anim pariatur cliche reprehenderit, enim eiusmod high life accusamus terry richardson ad squid. 3 wolf moon officia aute, non cupidatat skateboard dolor brunch. Food truck quinoa nesciunt laborum eiusmod. Brunch 3 wolf moon tempor, sunt"
},
{
"button" : "Button 2",
"body" : "aliqua put a bird on it squid single-origin coffee nulla assumenda shoreditch et. Nihil anim keffiyeh helvetica, craft beer labore wes anderson cred nesciunt sapiente ea proident. Ad vegan excepteur butcher vice lomo. Leggings occaecat"
},
{
"button" : "Button 3",
"body" : "craft beer farm-to-table, raw denim aesthetic synth nesciunt you probably haven't heard of them accusamus labore sustainable VHS."
},
{
"button" : "Button 4",
"body" : "Lorem ipsum dolor sit amet"
}
]
loadDom
function:
function loadDom() {
//Get the data from data.json
var xobj = new XMLHttpRequest();
xobj.overrideMimeType("application/json");
xobj.open('GET', 'data.json', true);
xobj.onreadystatechange = function() {
if (xobj.readyState == 4 && xobj.status == "200") {
//parse the json
let dataToLoad = JSON.parse(xobj.responseText);
//create the cards
dataToLoad.forEach((card, i) => {
createCard(i, card["button"], card["body"]);
});
}
};
xobj.send(null);
}
2. Inline Json
function loadDom() {
//Just specify the data that you want...
var dataToLoad = [{
"button": "Button 1",
"body": "Anim pariatur cliche reprehenderit, enim eiusmod high life accusamus terry richardson ad squid. 3 wolf moon officia aute, non cupidatat skateboard dolor brunch. Food truck quinoa nesciunt laborum eiusmod. Brunch 3 wolf moon tempor, sunt"
},
{
"button": "Button 2",
"body": "aliqua put a bird on it squid single-origin coffee nulla assumenda shoreditch et. Nihil anim keffiyeh helvetica, craft beer labore wes anderson cred nesciunt sapiente ea proident. Ad vegan excepteur butcher vice lomo. Leggings occaecat"
},
{
"button": "Button 3",
"body": "craft beer farm-to-table, raw denim aesthetic synth nesciunt you probably haven't heard of them accusamus labore sustainable VHS."
},
{
"button": "Button 4",
"body": "Lorem ipsum dolor sit amet"
}
];
//load the data from json and create the cards
dataToLoad.forEach((card, i) => {
createCard(i, card["button"], card["body"]);
});
}
function createCard(id, buttonText, cardBody) {
var card = document.getElementById("cardTemplate").content;
//clone the card template
var cln = card.cloneNode(true);
//create the custom heading and collapse id
let headingId = "heading-" + id;
let collapseId = "collapse-" + id;
//set all the attributes
cln.querySelectorAll(".card-header")[0].id = headingId;
cln.querySelectorAll(".card-header button")[0].setAttribute("data-target", "#" + collapseId);
cln.querySelectorAll(".card-header button")[0].setAttribute("aria-controls", collapseId);
cln.querySelectorAll(".card-body")[0].parentElement.id = collapseId;
cln.querySelectorAll(".card-body")[0].parentElement.setAttribute("aria-labelledby", headingId);
//set the content
cln.querySelectorAll(".card-header button")[0].innerHTML = buttonText;
cln.querySelectorAll(".card-body")[0].innerHTML = cardBody;
//add the card to the accordion
document.getElementById("accordion").appendChild(cln);
}
<!DOCTYPE html>
<html lang="en" dir="ltr">
<head>
<meta charset="utf-8">
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.0/css/bootstrap.min.css" integrity="sha384-9aIt2nRpC12Uk9gS9baDl411NQApFmC26EwAOH8WgZl5MYYxFfc+NcPb1dKGj7Sk" crossorigin="anonymous">
<script src="https://code.jquery.com/jquery-3.5.1.slim.min.js" integrity="sha384-DfXdz2htPH0lsSSs5nCTpuj/zy4C+OGpamoFVy38MVBnE+IbbVYUew+OrCXaRkfj" crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/umd/popper.min.js" integrity="sha384-Q6E9RHvbIyZFJoft+2mJbHaEWldlvI9IOYy5n3zV9zzTtmI3UksdQRVvoxMfooAo" crossorigin="anonymous"></script>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.5.0/js/bootstrap.min.js" integrity="sha384-OgVRvuATP1z7JjHLkuOU7Xw704+h835Lr+6QL9UvYjZE3Ipu6Tp75j7Bh/kR0JKI" crossorigin="anonymous"></script>
</head>
<body onload="loadDom();">
<div id="accordion">
<template id="cardTemplate">
<!-- Card -->
<div class="card">
<div class="card-header">
<h5 class="mb-0">
<button class="btn btn-link" data-toggle="collapse" aria-expanded="true" >
</button>
</h5>
</div>
<div class="collapse" data-parent="#accordion">
<div class="card-body">
</div>
</div>
</div>
<!-- /card -->
</template>
</div>
</body>
</html>
Hope this helps! If not, please comment
(For this example I borrowed @Polygnome's <head>
code)
Solution 4:[4]
As an extension of Polygnome's answer,
You can accomplish this in vanilla JavaScript using the following:
function appendCard(id, content) {
let el = document.querySelector(".card");
let content = document.createTextNode(`<div class="card">
<div class="card-header" id="heading-${id}">
<h5 class="mb-0">
<button class="btn btn-link collapsed" data-toggle="collapse" data-target="#collapse-${id}" aria-expanded="false" aria-controls="collapse-${id}">Collapsible Group Item #${id}</button>
</h5>
</div>
<div id="collapse-${id}" class="collapse" aria-labelledby="heading-${id}">
<div class="card-body">
${content}
</div>
</div>
</div>`);
el.appendChild(content);
}
Hope this helps!
Solution 5:[5]
answer to A- There's no way out with pure HTML and CSS, As HTML is the language where you define the content, that's why you have to repeat the same things 'n' times for 'n' number of results... But if you feel confused while giving each repeated content a new class and Id for its unique CSS, Then it is not required, as you can simply use :nth-child()
property... You can just copy and paste the code in HTML with same classes and still you can define different CSS to each one of them... CHECK :nth-child()
for more info !!
answer to B and C- Well, there is not much left that I can sill tell you now, as I am a bit late to the party and the answers have already been discussed by others, which I feel are absolutely correct and relatable... You can kindly check other answers from different users (like- Chiel, ROOT, Polygnome & Coder100) for desired results with the use of different languages and frameworks outside pure CSS and HTML...
Have a nice day to everyone!!
Regards,
Om Chaudhary
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 | |
Solution 2 | TreyA |
Solution 3 | |
Solution 4 | |
Solution 5 | Om_16 |