Scripts All in Header or Footer or Can They Be in Middle of Page

Reusable HTML Components – How to Reuse a Header and Footer on a Website

Imagine yous're building a site for a client, a small mom-and-pop store, that only has two pages.

That's not a lot. So when you end working on the landing page and beginning on the contact page, yous just create a new HTML file and copy over all the code from the first page.

The header and footer are already looking good, and all you need to practice is alter the rest of the content.

But what if your client wants 10 pages? Or xx? And they request modest changes to the header and footer throughout development.

Suddenly whatever change, no matter how pocket-size, has to exist repeated across all those files.

This is i of the major problems things like React or Handlebars.js solve: whatever code, especially structural things like a header or footer, can be written once and easily reused throughout a project.

Until recently it wasn't possible to utilize components in vanilla HTML and JavaScript. But with the introduction of Web Components, it's possible to create reusable components without using things similar React.

What Are Web Components?

Spider web Components are actually a drove of a few different technologies that allow y'all to create custom HTML elements.

Those technologies are:

  • HTML templates: Fragments of HTML markup using <template> elements that won't be rendered until they're appended to the page with JavaScript.
  • Custom elements: Widely supported JavaScript APIs that let you create new DOM elements. In one case yous create and annals a custom element using these APIs, yous tin can use it similarly to a React component.
  • Shadow DOM: A smaller, encapsulated DOM that is isolated from the main DOM and rendered separately. Any styles and scripts you create for your custom components in the Shadow DOM will non affect other elements in the main DOM.

We'll dive into each of these a bit more throughout the tutorial.

How to Utilise HTML Templates

The first slice of the puzzle is learning how to employ HTML templates to create reusable HTML markdown.

Let's wait at a unproblematic welcome message example:

                  <!DOCTYPE html> <html>   <head>     <meta charset="UTF-8">     <meta proper noun="viewport" content="width=device-width, initial-scale=1.0">     <meta http-equiv="X-UA-Uniform" content="ie=edge">     <link href="style.css" rel="stylesheet" type="text/css" />     <script src="alphabetize.js" blazon="text/javascript" defer></script>   </caput>   <body>     <template id="welcome-msg">       <h1>Hello, Earth!</h1>       <p>And all who inhabit it</p>     </template>   </body> <html>                                  
index.html

If you expect at the folio, neither the <h1> or <p> elements are rendered. Simply if you open the dev console, you'll see both elements have been parsed:

The developer console showing the welcome message template element.

To actually render the welcome message, you lot'll demand to use a fleck of JavaScript:

                  const template = document.getElementById('welcome-msg');  certificate.torso.appendChild(template.content);                                  
index.js
The browser showing the welcome message template element, and the actual welcome message content.

Even though this is a pretty uncomplicated example, you can already see how using templates makes it piece of cake to reuse code throughout a page.

The main issue is that, at least with the current example, the welcome message lawmaking is mixed in with the rest of the folio's content. If y'all want to change the welcome bulletin later, you'll need to change the lawmaking across multiple files.

Instead, you can pull the HTML template into the JavaScript file, so any folio the JavaScript is included in will render the welcome bulletin:

                  <!DOCTYPE html> <html>   <caput>     <meta charset="UTF-8">     <meta name="viewport" content="width=device-width, initial-scale=1.0">     <meta http-equiv="X-UA-Compatible" content="ie=border">     <link href="style.css" rel="stylesheet" type="text/css" />     <script src="index.js" type="text/javascript" defer></script>   </head>   <torso>          </body> <html>                                  
index.html
                  const template = document.createElement('template');  template.innerHTML = `   <h1>Hello, World!</h1>   <p>And all who inhabit information technology</p> `;  document.body.appendChild(template.content);                                  
alphabetize.js

Now that everything's in the JavaScript file, you don't need to create a <template> element – you could just every bit easily create a <div> or <span>.

However, <template> elements can be paired with a <slot> chemical element, which allows you to practise things similar change the text for elements within the <template>. It's a bit exterior the scope of this tutorial, so you can read more than about <slot> elements over on MDN.

How to Create Custom Elements

One thing you might have noticed with HTML templates is that it can exist tricky to insert your code in the right place. The earlier welcome bulletin case was just appended to the page.

If there was content already on the page, say, a banner paradigm, the welcome bulletin would appear beneath it.

As a custom element, your welcome message might look like this:

                <welcome-message></welcome-message>                              

And you can put information technology wherever y'all want on the page.

With that in mind, allow's take a look at custom elements and create our ain React-like header and footer elements.

Setup

For a portfolio site, yous might have some boilerplate code that looks like this:

                  <!DOCTYPE html> <html>   <head>     <meta charset="UTF-8">     <meta name="viewport" content="width=device-width, initial-scale=ane.0">     <meta http-equiv="X-UA-Compatible" content="ie=edge">     <link href="style.css" rel="stylesheet" type="text/css" />   </head>   <trunk>     <master>       <!-- Your page's content -->     </main>   </trunk> <html>                                  
index.html
                  * {   margin: 0;   padding: 0;   box-sizing: edge-box; }  html, body {   elevation: 100%; }  torso {   color: #333;   font-family unit: sans-serif;   display: flex;   flex-management: cavalcade; }  main {   flex: 1 0 motorcar; }                                  
style.css

Each page volition have the same header and footer, so it makes sense to create a custom chemical element for each of those.

Let's start with the header.

Ascertain a Custom Chemical element

Showtime, create a directory called components and inside that directory, create a new file called header.js with the following code:

                  class Header extends HTMLElement {   constructor() {     super();   } }                                  
components/header.js

It'south just a unproblematic ES5 Course declaring your custom Header component, with the constructor method and special super keyword. You lot can read more than nigh those on MDN.

By extending the generic HTMLElement class, you tin create any kind of element yous desire. Information technology's likewise possible to extend specific elements similar HTMLParagraphElement.

Register Your Custom Element

Earlier you tin can start using your custom element, you'll demand to register it with the customElements.ascertain() method:

                  class Header extends HTMLElement {   constructor() {     super();   } }  customElements.ascertain('header-component', Header);                                  
components/header.js

This method takes at least two arguments.

The first is a DOMString you lot'll use when adding the component to the folio, in this case, <header-component></header-component>.

The next is the component's class that you created earlier, here, the Header class.

The optional tertiary argument describes which existing HTML chemical element your custom element inherits backdrop from, for example, {extends: 'p'}. Simply we won't exist using this characteristic in this tutorial.

At that place are iv special lifecycle callbacks for custom elements that we can use to append header markdown to the page: connectedCallback, attributeChangeCallback, disconnectedCallback, and adoptedCallback.

Of these callbacks, connectedCallback is one of the nearly commonly used. connectedCallback runs each time your custom element is inserted into the DOM.

You can read more about the other callbacks hither.

For our elementary example, connectedCallback is enough to add a header to the page:

                  class Header extends HTMLElement {   constructor() {     super();   }    connectedCallback() {     this.innerHTML = `       <manner>         nav {           height: 40px;           display: flex;           align-items: center;           justify-content: center;           background-color:  #0a0a23;         }          ul {           padding: 0;         }                  a {           font-weight: 700;           margin: 0 25px;           color: #fff;           text-ornamentation: none;         }                  a:hover {           padding-lesser: 5px;           box-shadow: inset 0 -2px 0 0 #fff;         }       </fashion>       <header>         <nav>           <ul>             <li><a href="about.html">Near</a></li>             <li><a href="piece of work.html">Work</a></li>             <li><a href="contact.html">Contact</a></li>           </ul>         </nav>       </header>     `;   } }  customElements.define('header-component', Header);                                  
components/header.js

Then in index.html, add the components/header.js script and <header-component></header-component> just above the <primary> element:

                  <!DOCTYPE html> <html>   <caput>     <meta charset="UTF-8">     <meta proper noun="viewport" content="width=device-width, initial-scale=1.0">     <meta http-equiv="X-UA-Compatible" content="ie=border">     <link href="style.css" rel="stylesheet" type="text/css" />     <script src="components/header.js" type="text/javascript" defer></script>   </head>   <trunk>     <header-component></header-component>     <principal>       <!-- Your page's content -->     </main>   </torso> <html>                                  
alphabetize.html

And your reusable header component should be rendered to the page:

The browser with the header component.

Now adding a header to the page is as piece of cake equally adding a <script> tag pointing to components/header.js, and adding <header-component></header-component> wherever you want.

Note that, since the header and its styling are being inserted into the principal DOM direct, it'southward possible to style information technology in the style.css file.

But if you look at the header styles included in connectedCallback, they're quite general, and can affect other styling on the page.

For example, if we add Font Awesome and a footer component to alphabetize.html:

                  <!DOCTYPE html> <html>   <caput>     <meta charset="UTF-8">     <meta name="viewport" content="width=device-width, initial-calibration=1.0">     <meta http-equiv="10-UA-Compatible" content="ie=edge">     <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.xv.i/css/all.min.css" integrity="sha512-+4zCK9k+qNFUR5X+cKL9EIR+ZOhtIloNl9GIKS57V1MyNsYpYcUrUeQc9vNfzsWfV28IaLL3i96P9sdNyeRssA==" crossorigin="anonymous" />     <link href="way.css" rel="stylesheet" blazon="text/css" />     <script src="components/header.js" type="text/javascript" defer></script>     <script src="components/footer.js" type="text/javascript" defer></script>   </caput>   <body>     <header-component></header-component>     <main>       <!-- Your folio's content -->     </principal>     <footer-component></footer-component>   </body> <html>                                  
index.html
                  grade Footer extends HTMLElement {   constructor() {     super();   }    connectedCallback() {     this.innerHTML = `       <mode>         footer {           acme: 60px;           padding: 0 10px;           listing-style: none;           display: flex;           justify-content: space-between;           align-items: center;           groundwork-color: #dfdfe2;         }                  ul li {           listing-way: none;           display: inline;         }                  a {           margin: 0 15px;           color: inherit;           text-decoration: none;         }                  a:hover {           padding-bottom: 5px;           box-shadow: inset 0 -2px 0 0 #333;         }                  .social-row {           font-size: 20px;         }                  .social-row li a {           margin: 0 15px;         }       </manner>       <footer>         <ul>           <li><a href="about.html">Almost</a></li>           <li><a href="piece of work.html">Piece of work</a></li>           <li><a href="contact.html">Contact</a></li>         </ul>         <ul class="social-row">           <li><a href="https://github.com/my-github-profile"><i class="fab fa-github"></i></a></li>           <li><a href="https://twitter.com/my-twitter-profile"><i class="fab fa-twitter"></i></a></li>           <li><a href="https://www.linkedin.com/in/my-linkedin-profile"><i class="fab fa-linkedin"></i></a></li>         </ul>       </footer>     `;   } }  customElements.define('footer-component', Footer);                                  
components/footer.js

Here'south what the page would look like:

The browser with the header and footer components.

The styling from the footer component overrides the styling for the header, irresolute the color of the links. That's expected behavior for CSS, merely it would exist nice if each component'due south styling was scoped to that component, and wouldn't affect other things on the page.

Well, that's exactly where the Shadow DOM shines. Or shades? Anyway, the Shadow DOM can practice that.

How to Utilize the Shadow Dom with Custom Elements

The Shadow DOM acts equally a divide, smaller case of the main DOM. Rather than act every bit a copy of the main DOM, the Shadow DOM is more like a subtree just for your custom chemical element. Anything added to a Shadow DOM, peculiarly styles, are scoped that particular custom element.

In a fashion, it's similar using const and allow rather than var.

Let's commencement by refactoring the header component:

                  const headerTemplate = document.createElement('template');  headerTemplate.innerHTML = `   <way>     nav {       height: 40px;       display: flex;       align-items: center;       justify-content: center;       groundwork-colour:  #0a0a23;     }      ul {       padding: 0;     }          ul li {       listing-style: none;       display: inline;     }          a {       font-weight: 700;       margin: 0 25px;       color: #fff;       text-decoration: none;     }          a:hover {       padding-bottom: 5px;       box-shadow: inset 0 -2px 0 0 #fff;     }   </fashion>   <header>     <nav>       <ul>         <li><a href="about.html">About</a></li>         <li><a href="work.html">Work</a></li>         <li><a href="contact.html">Contact</a></li>       </ul>     </nav>   </header> `;  form Header extends HTMLElement {   constructor() {     super();   }    connectedCallback() {        } }  customElements.define('header-component', Header);                                  
components/header.js

The outset affair you lot need to do is to use the .attachShadow() method to attach a shadow root to your custom header component element. In connectedCallback, add the following code:

                  ... course Header extends HTMLElement {   constructor() {     super();   }    connectedCallback() {     const shadowRoot = this.attachShadow({ way: 'closed' });   } }  customElements.ascertain('header-component', Header);                                  
components/header.js

Notice that we're passing an object to .attachShadow() with an option, mode: 'airtight'. This just means that the header component's shadow DOM is inaccessible from external JavaScript.

If you'd similar to dispense the header component's shadow DOM afterwards with JavaScript outside the components/header.js file, merely change the option to mode: 'open'.

Finally, append shadowRoot to the page with the .appendChild() method:

                  ...  class Header extends HTMLElement {   constructor() {     super();   }    connectedCallback() {     const shadowRoot = this.attachShadow({ mode: 'closed' });      shadowRoot.appendChild(headerTemplate.content);   } }  customElements.define('header-component', Header);                                  
components/header.js

And now, since the header component'south styles are encapsulated in its Shadow DOM, the page should expect similar this:

The browser with the Shadow DOM header component and regular footer component.

And here's the footer component refactored to use the Shadow DOM:

                  const footerTemplate = document.createElement('template');  footerTemplate.innerHTML = `   <way>     footer {       height: 60px;       padding: 0 10px;       list-style: none;       display: flex;       flex-compress: 0;       justify-content: space-betwixt;       align-items: center;       groundwork-colour: #dfdfe2;     }      ul {       padding: 0;     }          ul li {       list-mode: none;       brandish: inline;     }          a {       margin: 0 15px;       color: inherit;       text-ornamentation: none;     }          a:hover {       padding-bottom: 5px;       box-shadow: inset 0 -2px 0 0 #333;     }          .social-row {       font-size: 20px;     }          .social-row li a {       margin: 0 15px;     }   </style>   <footer>     <ul>       <li><a href="about.html">About</a></li>       <li><a href="piece of work.html">Work</a></li>       <li><a href="contact.html">Contact</a></li>     </ul>     <ul class="social-row">       <li><a href="https://github.com/my-github-profile"><i class="fab fa-github"></i></a></li>       <li><a href="https://twitter.com/my-twitter-profile"><i class="fab fa-twitter"></i></a></li>       <li><a href="https://world wide web.linkedin.com/in/my-linkedin-profile"><i class="fab fa-linkedin"></i></a></li>     </ul>   </footer> `;  class Footer extends HTMLElement {   constructor() {     super();   }    connectedCallback() {     const shadowRoot = this.attachShadow({ way: 'closed' });      shadowRoot.appendChild(footerTemplate.content);   } }  customElements.define('footer-component', Footer);                                  
components/footer.js

But if you check on the folio, you lot'll detect that the Font Awesome icons are now missing:

The browser showing the Shadow DOM versions of both the header and footer components.

At present that the footer component is encapsulated within its own Shadow DOM, it no longer has access to the Font Crawly CDN link in index.html.

Let's take a quick look at why this is, and how to get Font Awesome working again.

Encapsulation and the Shadow DOM

While the Shadow DOM does prevent styles from your components from affecting the rest of the page, some global styles can yet leak through to your components.

In the examples above, this has been a useful feature. For case, the footer component inherits the color: #333 declaration that's set in style.css. This is because color is one of a handful of inheritable properties, along with font, font-family, direction, and more.

If you'd like to preclude this behavior, and style each component completely from scratch, y'all tin do that with just a few lines of CSS:

                :host {   all: initial;   display: block; }              

:host is a pseudo-selector that selects the element that's hosting the Shadow DOM. In this instance, that's your custom component.

And so the all: initial announcement sets all CSS properties dorsum to their initial value. And display: block does the same affair for the display property, and sets it back to the browser default, block.

For a full list of CSS inheritable properties, check out this respond on Stack Overflow.

How to Use Font Crawly With the Shadow DOM

Now you might be thinking, if font, font-family unit and other font-related CSS properties are inheritable properties, why doesn't Font Awesome load now that the footer component is using the Shadow DOM?

Information technology turns out that, for things like fonts and other avails, they need to be referenced in both the principal DOM and Shadow DOM to piece of work properly.

Fortunately in that location are a few simple ways to set up this.

Notation: All of these methods still require that Font Awesome is included in index.html with the link element as in the code snippets above.

The virtually straightforward fashion to go Font Crawly to work in your Shadow DOM component is to include a link to it within the component itself:

                  const footerTemplate = certificate.createElement('template');  footerTemplate.innerHTML = `   <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-crawly/5.xv.1/css/all.min.css" integrity="sha512-+4zCK9k+qNFUR5X+cKL9EIR+ZOhtIloNl9GIKS57V1MyNsYpYcUrUeQc9vNfzsWfV28IaLL3i96P9sdNyeRssA==" crossorigin="anonymous" />   <style>     footer {       top: 60px;       padding: 0 10px;       list-style: none; ...                
components/footer.js

Ane thing to note is, while it seems like you're causing the browser to load Font Awesome twice (once for the main DOM and again for the component), browsers are smart plenty not to fetch the aforementioned resources once more.

Here's the network tab showing that Chrome only fetches Font Awesome once:

The developer console network tab showing that Font Awesome only loads once.

#2: Import Font Awesome Inside Your Component

Next, you can use @import and url() to load Font Awesome into your component:

                const footerTemplate = document.createElement('template');  footerTemplate.innerHTML = `   <style>     @import url("https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.one/css/all.min.css");      footer {       height: 60px;       padding: 0 10px;       list-fashion: none; ...              

Note that the URL should be the same ane you lot're using in index.html.

#3: Use JavaScript to Dynamically Load Font Crawly to Your Component

Finally, the DRYest way to load Font Awesome within your component is to use a scrap of JavaScript:

                  ... grade Footer extends HTMLElement {   constructor() {     super();   }    connectedCallback() {     // Query the main DOM for FA     const fontAwesome = document.querySelector('link[href*="font-awesome"]');     const shadowRoot = this.attachShadow({ style: 'closed' });      // Conditionally load FA to the component     if (fontAwesome) {       shadowRoot.appendChild(fontAwesome.cloneNode());     }      shadowRoot.appendChild(footerTemplate.content);   } }  customElements.ascertain('footer-component', Footer);                
components/footer.js

This method is based on this reply on Stack Overflow, and works pretty simply. When the component loads, if a link element pointing to Font Awesome exists, then it'south cloned and appended to the component'due south Shadow DOM:

The developer console showing that Font Awesome was dynamically added to the Shadow DOM component.

Concluding Code

Hither'southward what the last code across all files looks similar, and using method #3 to load Font Awesome into the footer component:

                  <!DOCTYPE html> <html>   <head>     <meta charset="UTF-eight">     <meta name="viewport" content="width=device-width, initial-scale=ane.0">     <meta http-equiv="10-UA-Compatible" content="ie=edge">     <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-crawly/5.fifteen.1/css/all.min.css" integrity="sha512-+4zCK9k+qNFUR5X+cKL9EIR+ZOhtIloNl9GIKS57V1MyNsYpYcUrUeQc9vNfzsWfV28IaLL3i96P9sdNyeRssA==" crossorigin="anonymous" />     <link href="style.css" rel="stylesheet" type="text/css" />     <script src="components/header.js" blazon="text/javascript" defer></script>     <script src="components/footer.js" type="text/javascript" defer></script>   </caput>   <body>     <header-component></header-component>     <main>       <!-- Your folio'southward content -->     </main>     <footer-component></footer-component>   </body> <html>                                  
index.html
                  * {   margin: 0;   padding: 0;   box-sizing: border-box; }  html, body {   height: 100%; }  body {   color: #333;   font-family unit: sans-serif;   display: flex;   flex-direction: column; }  main {   flex: i 0 car; }                                  
manner.css
                  const headerTemplate = document.createElement('template');  headerTemplate.innerHTML = `   <style>     nav {       height: 40px;       brandish: flex;       align-items: center;       justify-content: center;       background-colour:  #0a0a23;     }      ul {       padding: 0;     }          ul li {       list-style: none;       display: inline;     }          a {       font-weight: 700;       margin: 0 25px;       color: #fff;       text-ornament: none;     }          a:hover {       padding-bottom: 5px;       box-shadow: inset 0 -2px 0 0 #fff;     }   </style>   <header>     <nav>       <ul>         <li><a href="about.html">Most</a></li>         <li><a href="work.html">Piece of work</a></li>         <li><a href="contact.html">Contact</a></li>       </ul>     </nav>   </header> `;  class Header extends HTMLElement {   constructor() {     super();   }    connectedCallback() {     const shadowRoot = this.attachShadow({ style: 'closed' });      shadowRoot.appendChild(headerTemplate.content);   } }  customElements.ascertain('header-component', Header);                                  
components/header.js
                  const footerTemplate = document.createElement('template');  footerTemplate.innerHTML = `   <mode>     footer {       height: 60px;       padding: 0 10px;       list-manner: none;       brandish: flex;       flex-shrink: 0;       justify-content: space-between;       marshal-items: center;       groundwork-colour: #dfdfe2;     }      ul {       padding: 0;     }          ul li {       list-fashion: none;       display: inline;     }          a {       margin: 0 15px;       color: inherit;       text-decoration: none;     }          a:hover {       padding-bottom: 5px;       box-shadow: inset 0 -2px 0 0 #333;     }          .social-row {       font-size: 20px;     }          .social-row li a {       margin: 0 15px;     }   </style>   <footer>     <ul>       <li><a href="about.html">Nigh</a></li>       <li><a href="piece of work.html">Piece of work</a></li>       <li><a href="contact.html">Contact</a></li>     </ul>     <ul class="social-row">       <li><a href="https://github.com/my-github-profile"><i class="fab fa-github"></i></a></li>       <li><a href="https://twitter.com/my-twitter-profile"><i course="fab fa-twitter"></i></a></li>       <li><a href="https://world wide web.linkedin.com/in/my-linkedin-profile"><i class="fab fa-linkedin"></i></a></li>     </ul>   </footer> `;  class Footer extends HTMLElement {   constructor() {     super();   }    connectedCallback() {     const fontAwesome = document.querySelector('link[href*="font-awesome"]');     const shadowRoot = this.attachShadow({ mode: 'closed' });      if (fontAwesome) {       shadowRoot.appendChild(fontAwesome.cloneNode());     }      shadowRoot.appendChild(footerTemplate.content);   } }  customElements.define('footer-component', Footer);                                  
components/footer.js

In Closing

We've covered a lot hither, and you lot might accept already decided just to use React or Handlebars.js instead.

Those are both great options!

Still, for a smaller projection where you'll only need a few reusable components, a whole library or templating language might be overkill.

Hopefully at present you have the confidence to create your own reusable HTML components. Now get out there and create something great (and reusable).



Acquire to lawmaking for free. freeCodeCamp's open source curriculum has helped more than forty,000 people go jobs equally developers. Get started

vinsonwhild1983.blogspot.com

Source: https://www.freecodecamp.org/news/reusable-html-components-how-to-reuse-a-header-and-footer-on-a-website/

0 Response to "Scripts All in Header or Footer or Can They Be in Middle of Page"

Post a Comment

Iklan Atas Artikel

Iklan Tengah Artikel 1

Iklan Tengah Artikel 2

Iklan Bawah Artikel