Reusing navbars

Tags

Log in

If you're a student, please log in, so you can get the most from this page.

Lesson contents

Navbar reuse = happiness

On most sites, the same, or a similar, navbar is used on all pages. You can copy-and-paste the same navbar code to each page. Makes sense.

Until you need to change the navbar. Add a new page that needs a navbar link? You need to change every page. All 392 of them, or whatever the number is.

What if you could have the navbar in a separate HTML file, maybe called top-menu.inc, and insert top-menu.inc into every page on the site? Woo-hoo! Then, when you want to add a menu item, change that one file top-menu.inc, and every page on the site changes.

(BTW, the inc in top-menu.inc stands for "include.")

That's what this lesson is about. It's a big productivity win.

DoggoLand example

Let's make a site about DoggoLand, a theme park.

Here are the pages:

Pages in the DoggoLand site

index.html is the home page. location.html has a map, and directions to the park. tickets.html is where you can buy tickets.

rides is a folder. It has three pages, each one about a different ride.

You can try the site. You'll see that each page has the same navbar:

Navbar

How it works

We make one file with the navbar code. Then, on each page, we leave a gap, where we want the navbar to appear. Then, when the page loads, we want the navbar code to be injected into the gap.

Navbar injected into pages

When the browser loads a page, we want it to run some JavaScript code, that will fetch top-menu.inc, and inject it into the page. You don't need to write JavaScript code. I've done that for you, and put it in a file. All you need to do, is use a script tag to load the file on each page.

Make a library folder

To start, we'll need a place for top-menu.inc to live. The file with the JS (JavaScript) will live in the same place. Let's make a library folder for our new stuff.

library folder

Make top-menu.inc with the navbar code

Let's make top-menu.inc, the file with the navbar HTML. This HTML will be inserted into each of your pages.

You learned to make navbar HTML in the previous lesson. Like this:

  • <nav class="navbar navbar-expand-lg navbar-light bg-light">
  •   <a class="navbar-brand" href="index.html" title="Home">DoggoLand</a>
  •   ...
  • </nav>

Important! This file contains just the code for the navbar. No body tag, and no start html tag, no CSS file link, nothing else. The file with the navbar code is never used by itself. It's inserted into another page, that already has the html tag, link tag, body tag, etc.

Make top-menu.inc in library, and put the navbar code in it:

library folder with navbar file

Copy the JavaScript file

Now, let's add the JavaScript code, in a file called loader.js in library.

JavaScript file added

To get a copy of loader.js, right-click on this link and choose "Save link as..." (Firefox and Chrome), "Save target as" (Edge), or whatever on another browser.

You don't need to change the file at all. Just save it into the library folder.

Preparing the webpages

Remember that we want to inject code into our pages.

Navbar injected into pages

We have top-menu.inc. We have the JavaScript code that will do the injection. Now, we need to prep our webpages, like index.html, dogarium.html, and tickets.html.

There are two steps.

  • Include the JavaScript code on the page.
  • Mark where you want the navbar to be injected.

Including the JavaScript code

We've been using an HTML template for our BS pages, with this at the bottom:

  •     <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.2.0/dist/js/bootstrap.bundle.min.js" integrity="sha384-A3rJD856KowSb7dwlZdYEkO39Gagi7vIsF0jrRAoQmDKKtQBHUuLZ9AsSv4jD4Xa" crossorigin="anonymous"></script>
  •   </body>
  • </html>

Let's add two new lines:

  •     <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.2.0/dist/js/bootstrap.bundle.min.js" integrity="sha384-A3rJD856KowSb7dwlZdYEkO39Gagi7vIsF0jrRAoQmDKKtQBHUuLZ9AsSv4jD4Xa" crossorigin="anonymous"></script>
  •     <script src="https://code.jquery.com/jquery-3.3.1.min.js"></script>
  •     <script src="library/loader.js"></script>
  •   </body>
  • </html>

One line adds loader.js from the library folder. The other loads jQuery, a popular JavaScript library, that loader.js uses.

The Angely Trap

Angely discovered a trap here. If you switch the order of the last two script tags...

  • <script src="library/loader.js"></script>
  • <script src="https://code.jquery.com/jquery-3.3.1.min.js"></script>

... you get an error. Check the console in the browser's dev tools (F12 on a PC) to see it. The error message will be something like "$ not defined." $ is defined in the JQuery file. If loader.js is listed first, it tries to access $ before it is defined.

So, keep the lines in this order:

  • <script src="https://code.jquery.com/jquery-3.3.1.min.js"></script>
  • <script src="library/loader.js"></script>

Thanks to the interpid Angely for discovering the Angely Trap.

Marking the injection point

Recall that BS uses rows and columns. Like this:

  • <body>
  •   <div class="container">
  •     <div class="row">
  •       <div class="col-sm-12">
  •         <!-- Navbar -->
  •       </div>
  •     </div>
  •     <div class="row">
  •       <div class="col-sm-12">
  •         <!-- Content -->
  •       </div>
  •     </div>
  •   </div>
  • </body>

We want the navbar in the first row. Let's mark the place where we want the navbar to be injected.

  • <body>
  •   <div class="container">
  •     <div class="row">
  •       <div class="col">
  •         <div data-replacement="library/top-menu.inc"></div>
  •       </div>
  •     </div>

data-replacement gives the name of the file to inject. loader.js (we added that JS file earlier, remember) will look for elements with the data-replacement property, and replace them with the file given.

Georgina
Georgina

You used a div there, not a nav.

  • <div data-replacement="top-menu.inc"></div>

Does that matter?

No. The file we're injecting, top-menu.inc, has the nav element.

Actually, this isn't just for navbars. It will inject any HTML. Have a photo of your fave doggo, that you want on your site, in many places? Make a file in the library folder, and inject it as needed.

For example, best-doggo.inc might have this:

  • <p>
  •   <img src="rosie1.jpg" alt="Best doggo!">
  • </p>

In your pages:

  • <div data-replacement="library/best-doggo.html"></div>

Use this as many times as you like.

If you get a better photo of your best dog, change the code in best-doggo.html:

  • <p>
  •   <img src="rosie-blue-ribbon.jpg" alt="Rosie gets the blue!">
  • </p>

All of your pages that inject best-doggo.html will use the new photo.

Summary so far

To reuse some HTML:

  • Make a library folder.
  • Make a file there with the HTML you want to reuse.
  • Put loader.js in library.
  • Include loader.js in each page (with a script tag).
  • Mark the element you would like to replace, with data-replacement="path-to-the-file".

Changing other site elements

Adela
Adela

A question. I'm making a site for a student org. We want to include the name and email of our membership person on the site, on maybe ten different pages. Trouble is, that person changes. Currently, it's Buffy Summers, but next year, it'll be Willow Rosenberg. That means we'll have to go through all the pages, and change the membership contact.

No big deal, but it takes time, and it would be easy to miss one. Could we use this inject-HTML-in-several-files thing, to make the site easier to change?

Yes, you could. You could make a file called membership-questions.inc, or something like that. Put it in library. It would have HTML, like this:

  • <p>
  •   For membership info, please contact Buffy Summers, at slayer83@sunnydale.edu.
  • </p>

When you want to use it on a page, type in this:

  • <p data-replacement="library/membership-questions.inc"></p>

Use the data-replacement thing as many times as you like, on as many pages as you like.

Next semester, update membership-questions.inc with the new contact info. Everything will change across the site, automatically.

Georgina
Georgina

Wow, that's so useful! I did an internship last summer, updating a website. I kept making the same changes all over the site. This would have saved a lot of time.

Right! And Benjamins. When a large site is set up this way, one person can do a job it might take two or three people to do otherwise.

Ray
Ray

Hey, I was thinking. Pretend I'm interviewing for a job, and they asked what I know about making websites. I can talk about HTML, OK. But what might impress them, is if I tell them about productivity hacks I learned.

Like using Bootstrap templates, with good a11y built-in. Making responsive sites, so one site looks good on phones, tablets, and PCs. No duplication of effort.

This reusing HTML thing is another one.

Good thinking, Ray! Talking about productivity is good to do in interviews. The technique we're using here is called code reuse by geeks. Talking about how code reuse makes sites cheaper to maintain, could impress the interviewers.

Georgina
Georgina

Yeah, that's a good idea, Ray.

It takes a server

Here's part of the code from loader.js:

  • $(replacementElement).load(pathToFile...

The load() method contacts the server the HTML file is from, and gets the file named in the variable pathToFile. There has to be a server to respond, for this to work.

If the HTML page is being served from your Reclaim Hosting account, no problem. There is a server. But suppose you opened the HTML file on your PC, by double-clicking on the file name in Explorer. In that case, your browser opens the file directly from your C: or D: drive, or whatever. There is no server involved, and nothing for load() to send its request to.

Exercise

The subfolder problem

There's just one more wrinkle to deal with. Remember that we have pages in a subfolder:

JavaScript file added

We want top-menu.inc to work on all the pages on our site, whether the page is in the root folder, like location.html is, or whether the page is in a subfolder, like dogarium.html is.

Here's part of top-menu.inc:

  • <nav class="navbar navbar-expand-lg navbar-light bg-light">
  •   <a class="navbar-brand" href="index.html" title="Home">DoggoLand</a>
  •   ...
  • </nav>
Reflect

If the code was in tickets.html, would the a tag work? If the code was in dogarium.html, would the a tag work?

If you were logged in as a student, the lesson would pause here, and you'd be asked to type in a response. If you want to try that out, ask for an account on this site.
Adela
Adela

It looks OK...

No, wait. It says href="index.html". That would work for tickets.html, because tickets.html and index.html are in the same folder.

But href="index.html" wouldn't work for dogarium.html, because dogarium.html is in a subfolder. Then the link is in dogarium.html, it needs to be href="../index.html". Go up a level, then look for index.html.

Right! The links in top-menu.inc should change, depending on whether the page we're inserting top-menu.inc into is in a subfolder.

Marcus
Marcus

Argh! That sounds hard!

No worries. loader.js know how to deal with this. If the page you want to inject the HTML into is in a subfolder, just tell loader.js the path to the site root. So, dogarium.html is inside rides:

JavaScript file added

The path from dogarium.html to the site root is ../, that is, up one level. In dogarium.html, when you tell loader.js to inject top-menu.inc, you add something:

  • <body>
  •   <div class="container">
  •     <div class="row">
  •       <div class="col">
  •         <nav data-replacement="../library/top-menu.inc" data-path-to-root="../">
  •         </nav>
  •       </div>
  •     </div>

When you use the data-replacement, you can add the data-path-to-root. loader.js will adjust the paths in top-menu.inc, adding ../ to them all.

Well, almost all the paths. It won't adjust absolute paths, like this:

  • <a class="nav-link" href="https://www.google.com/search?tbm=isch&q=cute+goat">Cute goats</a>

This link searches Google for images of cute goats. Adding ../ to the front would break the link. So, loader.js won't adjust links that start with http or https.

You can also tell loader.js to not adjust individual links, like this:

  • <a class="nav-link" href="monkey-mania.html" data-no-update>Location</a>

data-no-update tells loader.js not to change the link. I'm not sure when you would use that, but it lets you skip links, when needed.

One last thing. The pages in the folder need to be able to find loader.js. You have to change the line that loads loader.js into your pages. From this...

  • <script src="library/loader.js"></script>

... to this...

  • <script src="../library/loader.js"></script>
Marcus
Marcus

Oh, I get it! The pages in the folder must go up a level, then down into the library, to find loader.js.

Loading loader.js

Right! You got it!

Also notice:

  • <body>
  •   <div class="container">
  •     <div class="row">
  •       <div class="col">
  •         <nav data-replacement="../library/top-menu.inc" data-path-to-root="../">
  •         </nav>
  •       </div>
  •     </div>

The browser has to go up a level, then down into library to find top-menu.inc.

Video lesson

Here's a video version of some of the stuff above.

Exercise

Exercise

Code reuse with path adjustment

Log in

If you're a student, please log in, so you can get the most from this page.

Earlier, you made a site about two yummy things. Now make a similar site, about yucky things.

A good way to do it is to copy the folder you made earlier. Then change the bits you need.

Here's my site's home page:

Home page

I put three things. You can have two, if you want.

Put your pages about yucky things in the subfolder yucky-things. Here's my file tree:

File tree

Reuse the navbar and footer. For example, here's what happens if I change the text in footer.html:

Footer changed

Submit your URL as usual.

Up next

We'll end the course by looking at two more useful BS components.