Collapse med JavaScript i Bootstrap 5

Permalänk

Collapse med JavaScript i Bootstrap 5

Fick för några dagar sedan för mig att "uppdatera" ett litet webbprojekt jag håller på med från att nyttja Bootsrap 4 till nya Bootstrap 5. Fick då lite problem med deras Collapse-funktion som jag gärna vill styra med hjälp av Javascript.

Har fått till att den öppnar och stänger vissa delar när jag klickar, men det jag vill få till är att den samtidigt som jag öppnar en del även stänger alla andra delar som är öppna. Med Bootsrap 4 kunde detta lösas med hjälp av följande script:

function collapse(n) { $(".collapse").collapse('hide'); $("#collapse"+n).collapse('toggle'); }

Med Bootsrap 5 funkar inte det och mina kunskaper räcker inte riktigt till för att kunna få till hur jag ska lösa det, trots hjälp av deras dokumentation: https://getbootstrap.com/docs/5.0/components/collapse/#via-javascript

Kan någon ge mig lite vägledning om hur jag ska göra?
Förenklat exempel på min HTML-kod:

<html><head> <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.1/dist/css/bootstr..." rel="stylesheet"> <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.1/dist/js/bootstra..."></script> <script> function collapse(n) { var myCollapse = document.getElementById('collapse'+n) var bsCollapse = new bootstrap.Collapse(myCollapse, { toggle: true }) } </script> </head> <body> <h1 onclick="collapse(1);">Rubrik 1</h1><br> <div class="collapse" id="collapse1"> Undertext till rubrik ett </div> <br><br> <h1 onclick="collapse(2);">Rubrik 2</h1><br> <div class="collapse" id="collapse2"> Undertext till rubrik två </div> <br><br> <h1 onclick="collapse(3);">Rubrik 3</h1><br> <div class="collapse" id="collapse3"> Undertext till rubrik tre </div> </body> </html>

Permalänk
Medlem

Dokumentationen du länkade till föreslår att börja så här:

var collapseElementList = [].slice.call(document.querySelectorAll('.collapse')) var collapseList = collapseElementList.map(function (collapseEl) { return new bootstrap.Collapse(collapseEl) })

Första raden hittar alla .collapse-element och gör en array av det.

Sen gör man en ny array utifrån elementen. Den nya arrayen innehåller bootstrap.Collapse-instanser – en för varje element. På en sådan instans kan man till exempel göra .hide() för att stänga.

Så jag lade in det i din kod. Dock blev collapseElementList en tom array, för den koden körs före alla elementen nedanför hunnit skapats. Så jag flyttade hela script-taggen till slutet istället.

Då återstår bara att uppdatera din collapse-funktion.

För att stänga allihop:

collapseList.forEach(function (collapse) { collapse.hide() })

Och sen för att öppna den man tryckte på:

collapseList[n - 1].show()

Det blir n minus ett för att arrays börjar på 0, inte 1.

Allt på en gång:

<html><head> <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.1/dist/css/bootstr..." rel="stylesheet"> <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.1/dist/js/bootstra..."></script> </head> <body> <h1 onclick="collapse(1);">Rubrik 1</h1><br> <div class="collapse"> Undertext till rubrik ett </div> <br><br> <h1 onclick="collapse(2);">Rubrik 2</h1><br> <div class="collapse"> Undertext till rubrik två </div> <br><br> <h1 onclick="collapse(3);">Rubrik 3</h1><br> <div class="collapse"> Undertext till rubrik tre </div> <script> var collapseElementList = [].slice.call(document.querySelectorAll('.collapse')) var collapseList = collapseElementList.map(function (collapseEl) { return new bootstrap.Collapse(collapseEl, { toggle: false }) }) function collapse(n) { collapseList.forEach(function (collapse) { collapse.hide() }) collapseList[n - 1].show() } </script> </body> </html>

Inte den finaste kod jag skrivit, men nåt i den stilen borde hjälpa.

Permalänk

@lydell, vilket hjälte du är!! Stort tack för ditt fina exempel och din förklaring. Jag tror att en stor miss jag gjorde när jag testade mig fram var att jag framför allt körde scriptet från början istället för i slutet. Det funkar ju faktiskt som jag vill, MEN i mitt exempel hade jag radat upp varje collapse-div i ordning 1,2,3 osv... I Verkligenheten kommer det att vara lite slumpat vilket ID varje div får så måste styra det lite mer exakt.

Har testat mig fram lite och fått fram en kod som fungerar som jag vill. Om du orkar vore jag tacksam om du skulle vilja ta en snabb titt på om jag tänkt rätt eller om jag bör tänka annorlunda. Även fast det fungerar så vill jag gärna veta om jag tänker fel eller kan göra det snyggare på något sätt, bara för att lära mig för framtida projekt?

<html><head> <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.1/dist/css/bootstr..." rel="stylesheet"> <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.1/dist/js/bootstra..."></script> </head> <body> <h1 onClick="collapse(12);">Rubrik 1</h1><br> <div class="collapse" id="collapse12"> Undertext till rubrik ett </div> <br><br> <h1 onClick="collapse(21);">Rubrik 2</h1><br> <div class="collapse" id="collapse21"> Undertext till rubrik två </div> <br><br> <h1 onClick="collapse(31);">Rubrik 3</h1><br> <div class="collapse" id="collapse31"> Undertext till rubrik tre </div> <script> var collapseElementList = [].slice.call(document.querySelectorAll('.collapse')) var collapseList = collapseElementList.map(function (collapseEl) { return new bootstrap.Collapse(collapseEl, { toggle: false }) }) function collapse(n) { var myCollapse = document.getElementById('collapse'+n) new bootstrap.Collapse(myCollapse) collapseList.forEach(function (collapse) { collapse.hide() }) } </script> </body> </html>

Permalänk
Medlem

Poängen med collapseList är att man skapar alla sina bootstrap.Collapse vid start. Sen skapar man inga fler, utan använder bara de man har. Detta är för att varje instans håller koll på sitt state – om den är öppen eller inte och så vidare. Så jag tror inte det är en bra idé att köra new bootstrap.Collapse() igen i collapse-funktionen (som körs vid varje klick).

Det du istället behöver är att kunna gå från ett elements ID till dess bootstrap.Collapse-instans. Det kan vi lösa genom att spara alla instanserna i ett objekt/map istället för en array, så man kan slå upp via ID istället för index.

Det här skapar ett tomt objekt:

var collapseMap = {}

Det här stoppar in saker i objektet, ett efter ett. Vi skapar en bootstrap.Collapse-instans för varje element, och använder dess ID som nyckel:

collapseElementList.forEach(function (collapseEl) { collapseMap[collapseEl.id] = new bootstrap.Collapse(collapseEl, { toggle: false }) })

I collapse-funktionen behöver vi uppdatera lite. Man går igenom ”allt i ett objekt” lite annorlunda än för än array:

for (var id in collapseMap) { collapseMap[id].hide() }

Och här är själva grejen: Vi kan nu slå upp via ID och öppna den klickade sektionen:

collapseMap['collapse' + n].show()

Allt på en gång:

<html><head> <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.1/dist/css/bootstr..." rel="stylesheet"> <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.1/dist/js/bootstra..."></script> </head> <body> <h1 onClick="collapse(12);">Rubrik 1</h1><br> <div class="collapse" id="collapse12"> Undertext till rubrik ett </div> <br><br> <h1 onClick="collapse(21);">Rubrik 2</h1><br> <div class="collapse" id="collapse21"> Undertext till rubrik två </div> <br><br> <h1 onClick="collapse(31);">Rubrik 3</h1><br> <div class="collapse" id="collapse31"> Undertext till rubrik tre </div> <script> var collapseElementList = [].slice.call(document.querySelectorAll('.collapse')) var collapseMap = {} collapseElementList.forEach(function (collapseEl) { collapseMap[collapseEl.id] = new bootstrap.Collapse(collapseEl, { toggle: false }) }) function collapse(n) { for (var id in collapseMap) { collapseMap[id].hide() } collapseMap['collapse' + n].show() } </script> </body> </html>