How to use multiple galleries in bootstrap 5.1 tabs
This is quite a long post, so get a drink, sit down and enjoy.
What does it do?
This solution allows you to load multiple galleries into the same page, but inside tabs. I am using bootstrap 5,1, but is should work with any tab implementation.
Doesn't sound hard.
That's what I thought. Simply load the galleries inside the tab contents when the page loads.
Job done, right?
Wrong!
What happens is Juicebox loads the gallery responsively into the available space, but a tab that isn't active is tiny. So the gallery loads into a tiny space and looks terrible. If you specify the size of the gallery it is no longer responsive, and this was not an acceptable compromise.
So what next?
Search the forums for a solution. There are a couple of posts, with solutions, that helped. But I wanted something simpler, easier to implement, and to understand (I'm not a great javascript developer, so simple is my goal). Plus I might have to support it in future, so make my future job as easy as possible!
So how did you do it?
Basically I don't load the gallery until the tab is activated - in this case when the tab button is clicked. This means the availabe space to load the gallery into is responsive to the browser/screen size.
OK, but how did you do that?
Firstly I used a function to load the gallery when it is called, I got this from another forum post, and repurposed it to my needs. I put it in the <head> of the page, so it is available as soon as it is needed.
<script>
function loadGallery(base, container) {
new juicebox({
baseUrl : base,
containerId: container,
galleryWidth: "100%",
galleryHeight: "100%",
backgroundColor: "Transparent"
});
}
</script>
When the loadGallery function is called it will load a juicebox gallery from the given location into the specified container. This is default juicebox way of loading a gallery, and is well documented in the juicebox support documentation.
Additionally I set width and height to 100%, maximising the size the gallery is displayed at based on the container size, screen size, and browser size.
Finally I set the background colour to transparent, I do this so there is no coloured box seen before the gallery loads. This is personal choice, so you can set whatever colour you like.
No, there's more.
I am using Bootstrap 5.1 in this project, so all layout and class names are specific to a default bootstrap implementation. If you want to do the same, there is lots of information and examples on the bootstrap website. Please have a look at that before using it.
I am using the default bootstrap CSS and javavscript from their CDN in this example.
Oh, so I need to understand bootstrap 5.1 as well.
A little, yes. But it is not essential, as the example web page (below) is well laid out and pretty basic, as well as having some comments in it.
OK, get on with it.
Alright, here we go.
First you set up the tabs. In bootstrap this can be done in many ways, but my preferred method is to use HTML buttons in an unordered list.
Here is the HTML, the important parts for juicebox are the button id names, as we will use these later to decide when to load the galleries. The HTML layout and classes are bootstrap defaults, and are important for functionality and styling.
<!-- List of 3 tabs as buttons, as per bootstrap examples, make sure the label names are consistent, eg gallery1 -->
<ul class="nav nav-tabs mt-3 pt-3" id="myTab" role="tablist">
<li class="nav-item" role="presentation">
<button class="nav-link active" id="gallery1-tab" data-bs-toggle="tab" data-bs-target="#gallery1" type="button" role="tab" aria-controls="gallery1" aria-selected="true">Gallery 1</button>
</li>
<li class="nav-item" role="presentation">
<button class="nav-link" id="gallery2-tab" data-bs-toggle="tab" data-bs-target="#gallery2" type="button" role="tab" aria-controls="gallery2" aria-selected="false">Gallery 2</button>
</li>
<li class="nav-item" role="presentation">
<button class="nav-link" id="gallery3-tab" data-bs-toggle="tab" data-bs-target="#gallery3" type="button" role="tab" aria-controls="gallery3" aria-selected="false">Gallery 3</button>
</li>
</ul>
<!-- End of tab list -->
The aria stuff you see is related to accessibility, and is also part of the default bootstrap examples - in addition to being very important for many users. It is easy to design this in from the start, and I always recommend it.
Great, looks pretty straightforward. What next?
Next we define the content areas to be displayed in each tab. Again we use the default bootstrap HTML and CSS.
As you can see there are 3 tab panes, each in their own div. The first one is the one that is active when the page loads. For this reason it is treated it differently from the other two tabs.
Each tab has a title and description, but these are optional.
After that, in the first tab we load the juicebox javascript. Change the path to wherever you have installed it, without this none of the galleries will work.
Then the loadGallery function (which you see earlier in this post) is called - like this
loadGallery('/images/gallery1/', 'juicebox-container1');
The first argument '/images/gallery1/' is the location of the config.xml file relating to that gallery. It is also where I put all the images for that gallery, but how you configure your site is up to you.
The second argument 'juicebox-container1' is the id of the div into which the gallery will be loaded. This is very important, get this wrong and the gallery will not be displayed.
The two other tab panes have much less content. The optional title and description, obviously, and the div with the id for loading a gallery into.
I have been very creative and set the div id's to id="juicebox-container1", id="juicebox-container2", and id="juicebox-container3" - it make it easy to read, track and extend.
<!-- Tab content -->
<div class="tab-content" id="myTabContent">
<!-- default tab, all scripts run when the page loads -->
<div class="tab-pane fade show active" id="gallery1" role="tabpanel" aria-labelledby="gallery1-tab">
<h2>Gallery 1 title</h2>
<p>Gallery description</p>
<!-- The id of this div is how juicebox knows where to display the gallery -->
<div class="border-top mt-3 pt-3" id="juicebox-container1"></div>
<!-- load the juicebox javascript before you call it -->
<script src="/scripts/juicebox_lite_1.5.1/web/jbcore/juicebox.js"></script>
<!-- call the funtion (defined in the page head) to load the gallery -->
<!-- into the specified div id, which is the one we are inside right now -->
<script>
loadGallery('/images/gallery1/', 'juicebox-container1');
</script>
</div>
<!-- Tab 2, no content until the tab button is clicked -->
<div class="tab-pane fade" id="gallery2" role="tabpanel" aria-labelledby="gallery2-tab">
<h2>Gallery 2 title</h2>
<p>Gallery description</p>
<!-- empty div, where the gallery will be loaded when the tab button is clicked-->
<div class="border-top mt-3 pt-3" id="juicebox-container2"></div>
</div>
<!-- Tab 3, no content until the tab button is clicked -->
<div class="tab-pane fade" id="gallery3" role="tabpanel" aria-labelledby="gallery3-tab">
<h2>Gallery 3 title</h2>
<p>Gallery description</p>
<!-- empty div, where the gallery will be loaded when the tab button is clicked-->
<div class="border-top mt-3 pt-3" id="juicebox-container3"></div>
</div>
</div>
<!-- End of tab content -->
Again, pretty basic HTML. But how do you load the galleries into the tabs?
With a little bit of javascript. Don't worry, it is really easy to follow.
First I "listen" for anyone clicking one of the tab buttons (based on their id eg gallery2-tab ), and call a simple function when it is. It is not necessary to listen for tab 1 to be clicked as it is always loaded when the page is loaded.
Each function calls the previously defined loadGallery function, using the location of the gallery and the id of the div to display the gallery in.
<script>
document.getElementById("gallery2-tab").onclick = function() {showGallery2()};
document.getElementById("gallery3-tab").onclick = function() {showGallery3()};
function showGallery2() {
loadGallery('/images/gallery2/', 'juicebox-container2');
}
function showGallery3() {
loadGallery('/images/gallery3/', 'juicebox-container3');
}
</script>
As you can see, the id of the buttons that were defined earlier are now used. Told you they were important.
Yeah, simple, even I can understand that. But why use functions with only 1 line in them?
You're right, I could have called the loadGallery function as part of the onclick listener. However, I prefer to use a function here because it is easy to add functionality and customisations if needed in the future.
Thanks, is that it then?
Yes, that's all there is to it. Below is a whole, fully functional web page, so you can see it all in one place. I find this useful, so others may feel the same.
If you copy this and want to test it, you will have to either put your images, config files and juicebox.js in the same places I have used or change the locations in the file.
Gallery1 config file and images are in /images/gallery1/
Gallery2 config file and images are in /images/gallery2/
Gallery3 config file and images are in /images/gallery3/
juicebox.js is in /scripts/juicebox_lite_1.5.1/web/jbcore/juicebox.js
I hope this helps others to implement a multiple gallery solution in tabs.
<!doctype html>
<html lang="en">
<head>
<!-- Required meta tags -->
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<!-- Bootstrap CSS -->
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.0/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-KyZXEAg3QhqLMpG8r+8fhAXLRk2vvoC2f3B09zVXn8CA5QIVfZOJ3BCsw2P0p/We" crossorigin="anonymous">
<!-- gallery javascript -->
<!-- Use this function to load the gallery into the available space in the container-->
<!-- This is useful when using tabs, as you can load it after the tab is clicked -->
<!-- When calling this function pass in the folder name where the juicebox config file is loacated -->
<!-- and the id of the div you want the gallery to be shown in -->
<script>
function loadGallery(base, container) {
new juicebox({
baseUrl : base,
containerId: container,
galleryWidth: "100%",
galleryHeight: "100%",
backgroundColor: "Transparent"
});
}
</script>
<!-- end gallery javascript -->
<title>Tabbed Gallery Example</title>
</head>
<body>
<!-- start of the page container, essential for bootstrap responsive design -->
<div class="container">
<header>
<!-- Put your menu here -->
</header>
<h1>Gallery with tabs</h1>
<!-- List of 3 tabs as buttons, as per bootstrap examples, make sure the label names are consistent, eg gallery1 -->
<ul class="nav nav-tabs mt-3 pt-3" id="myTab" role="tablist">
<li class="nav-item" role="presentation">
<button class="nav-link active" id="gallery1-tab" data-bs-toggle="tab" data-bs-target="#gallery1" type="button" role="tab" aria-controls="gallery1" aria-selected="true">Gallery 1</button>
</li>
<li class="nav-item" role="presentation">
<button class="nav-link" id="gallery2-tab" data-bs-toggle="tab" data-bs-target="#gallery2" type="button" role="tab" aria-controls="gallery2" aria-selected="false">Gallery 2</button>
</li>
<li class="nav-item" role="presentation">
<button class="nav-link" id="gallery3-tab" data-bs-toggle="tab" data-bs-target="#gallery3" type="button" role="tab" aria-controls="gallery3" aria-selected="false">Gallery 3</button>
</li>
</ul>
<!-- End of tab list -->
<!-- Tab content -->
<div class="tab-content" id="myTabContent">
<!-- default tab, all scripts run when the page loads -->
<div class="tab-pane fade show active" id="gallery1" role="tabpanel" aria-labelledby="gallery1-tab">
<h2>Gallery 1 title</h2>
<p>Gallery description</p>
<!-- The id of this div is how juicebox knows where to display the gallery -->
<div class="border-top mt-3 pt-3" id="juicebox-container1"></div>
<!-- load the juicebox javascript before you call it -->
<script src="/scripts/juicebox_lite_1.5.1/web/jbcore/juicebox.js"></script>
<!-- call the funtion (defined in the page head) to load the gallery -->
<!-- into the specified div id, which is the one we are inside right now -->
<script>
loadGallery('/images/gallery1/', 'juicebox-container1');
</script>
</div>
<!-- Tab 2, no content until the tab button is clicked -->
<div class="tab-pane fade" id="gallery2" role="tabpanel" aria-labelledby="gallery2-tab">
<h2>Gallery 2 title</h2>
<p>Gallery description</p>
<!-- empty div, where the gallery will be loaded when the tab button is clicked-->
<div class="border-top mt-3 pt-3" id="juicebox-container2"></div>
</div>
<!-- Tab 2, no content until the tab button is clicked -->
<div class="tab-pane fade" id="gallery3" role="tabpanel" aria-labelledby="gallery3-tab">
<h2>Gallery 3 title</h2>
<p>Gallery description</p>
<!-- empty div, where the gallery will be loaded when the tab button is clicked-->
<div class="border-top mt-3 pt-3" id="juicebox-container3"></div>
</div>
</div>
<!-- End of tab content -->
<!-- Use JS to load the gallery after the tab is clicked, or they don't load properly -->
<!-- Runs the specified function only after the tab button with the given id is clicked -->
<!-- Each function loads the defined gallery into the specified div id-->
<script>
document.getElementById("gallery2-tab").onclick = function() {showGallery2()};
document.getElementById("gallery3-tab").onclick = function() {showGallery3()};
function showGallery2() {
loadGallery('/images/gallery2/', 'juicebox-container2');
}
function showGallery3() {
loadGallery('/images/gallery3/', 'juicebox-container3');
}
</script>
<!-- footer -->
<footer>
<!-- Put your footer here -->
</footer>
<!-- end of footer -->
</div>
<!-- end of page container -->
<!-- Bootstrap Javascript Bundle with Popper -->
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.0/dist/js/bootstrap.bundle.min.js" integrity="sha384-U1DAWAznBHeqEIlVSCgzq+c9gqGAJn5c/t99JyeKa9xxaYpSvHU5awsuZVVFIhvj" crossorigin="anonymous"></script>
</body>
</html>