Imaginați-vă că construiți un site pentru un client, un mic magazin mom-and-pop, care are doar două pagini.

Asta nu e mult. Deci, atunci când terminați de lucrat pe pagina de destinație și începeți pe pagina de contact, trebuie doar să creați un nou fișier HTML și să copiați tot codul de pe prima pagină.

Antetul și subsolul arată deja bine și tot ce trebuie să faceți este să schimbați restul conținutului.

Dar dacă clientul dvs. dorește 10 pagini? Sau 20? Și solicită modificări minore ale antetului și subsolului pe tot parcursul dezvoltării.

Dintr-o dată, orice modificare, oricât de mică, trebuie repetată în toate acele fișiere.

Aceasta este una dintre problemele majore rezolvate de lucruri precum React sau Handlebars.js: orice cod, în special lucruri structurale precum un antet sau un subsol, poate fi scris o dată și ușor reutilizat pe tot parcursul unui proiect.

Până de curând nu era posibil să se utilizeze componente în HTML vanilat și JavaScript. Dar odată cu introducerea componentelor web, este posibil să creați componente reutilizabile fără a utiliza lucruri precum React.

Ce sunt componentele web?

Componentele web sunt de fapt o colecție de câteva tehnologii diferite care vă permit să creați elemente HTML personalizate.

Aceste tehnologii sunt:

  • Șabloane HTML: Fragmente de marcare HTML folosind <template> elemente care nu vor fi redate până când nu sunt adăugate la pagină cu JavaScript.
  • Elemente personalizate: API-uri JavaScript acceptate pe scară largă, care vă permit să creați elemente DOM noi. Odată ce creați și înregistrați un element personalizat utilizând aceste API-uri, îl puteți utiliza în mod similar cu o componentă React.
  • Shadow DOM: Un DOM mai mic, încapsulat, care este izolat de DOM-ul principal și redat separat. Orice stiluri și scripturi pe care le creați pentru componentele personalizate din Shadow DOM nu vor afecta alte elemente din DOM-ul principal.

Ne vom scufunda în fiecare dintre acestea un pic mai mult pe parcursul tutorialului.

Cum se utilizează șabloanele HTML

Prima piesă a puzzle-ului este învățarea modului de utilizare a șabloanelor HTML pentru a crea reducere HTML reutilizabilă.

Să vedem un exemplu simplu de mesaj de întâmpinare:

<!DOCTYPE html>
<html>
  <head>
    <link href="https://www.freecodecamp.org/news/reusable-html-components-how-to-reuse-a-header-and-footer-on-a-website/styles.css" rel="stylesheet" type="text/css" />
    <script src="index.js" type="text/javascript" defer></script>
  </head>
  <body>
    <template id="welcome-msg">
      <h1>Hello, World!</h1>
      <p>And all who inhabit it</p>
    </template>
  </body>
<html>
index.html

Dacă vă uitați la pagină, nici <h1> sau <p> elementele sunt redate. Dar dacă deschideți consola dev, veți vedea că ambele elemente au fost analizate:

Componente HTML reutilizabile Cum se reutilizeaza un antet si

Pentru a reda efectiv mesajul de întâmpinare, va trebui să utilizați un pic de JavaScript:

const template = document.getElementById('welcome-msg');
document.body.appendChild(template.content);
script.js
1611046026 138 Componente HTML reutilizabile Cum se reutilizeaza un antet si

Chiar dacă acesta este un exemplu destul de simplu, puteți vedea deja cum utilizarea șabloanelor facilitează reutilizarea codului pe o pagină.

Problema principală este că, cel puțin cu exemplul actual, codul mesajului de întâmpinare este amestecat cu restul conținutului paginii. Dacă doriți să modificați mesajul de întâmpinare mai târziu, va trebui să schimbați codul în mai multe fișiere.

În schimb, puteți trage șablonul HTML în fișierul JavaScript, astfel încât orice pagină în care este inclus JavaScript va reda mesajul de întâmpinare:

<!DOCTYPE html>
<html>
  <head>
    <link href="https://www.freecodecamp.org/news/reusable-html-components-how-to-reuse-a-header-and-footer-on-a-website/styles.css" rel="stylesheet" type="text/css" />
    <script src="index.js" type="text/javascript" defer></script>
  </head>
  <body>
      
  </body>
<html>
index.html
const template = document.createElement('template');
template.innerHTML = `
  <h1>Hello, World!</h1>
  <p>And all who inhabit it</p>
`

document.body.appendChild(template.content);
index.js

Acum că totul este în fișierul JavaScript, nu este nevoie să creați un fișier <template> element – ați putea crea la fel de ușor un <div> sau <span>.

In orice caz, <template> elementele pot fi asociate cu un <slot> element, care vă permite să faceți lucruri precum schimbarea textului pentru elemente din <template>. Este puțin în afara sferei acestui tutorial, astfel încât să puteți citi mai multe despre <slot> elemente peste MDN.

Cum se creează elemente personalizate

Un lucru pe care l-ați fi observat cu șabloanele HTML este că poate fi dificil să introduceți codul în locul potrivit. Exemplul de mesaj de întâmpinare anterior a fost adăugat doar la pagină.

Dacă deja există conținut pe pagină, să zicem, o imagine a bannerului, mesajul de întâmpinare va apărea sub aceasta.

Ca element personalizat, mesajul dvs. de bun venit ar putea arăta astfel:

<welcome-message></welcome-message>

Și îl puteți pune oriunde doriți pe pagină.

Având în vedere acest lucru, să aruncăm o privire asupra elementelor personalizate și să creăm propriile noastre elemente antet și subsol de tip React.

Înființat

Pentru un site de portofoliu, este posibil să aveți un cod boilerplate care arată astfel:

<!DOCTYPE html>
<html>
  <head>
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.1/css/all.min.css" integrity="sha512-+4zCK9k+qNFUR5X+cKL9EIR+ZOhtIloNl9GIKS57V1MyNsYpYcUrUeQc9vNfzsWfV28IaLL3i96P9sdNyeRssA==" crossorigin="anonymous" />
    <link href="https://www.freecodecamp.org/news/reusable-html-components-how-to-reuse-a-header-and-footer-on-a-website/styles.css" rel="stylesheet" type="text/css" />
  </head>
  <body>
    <main>
      <!-- Your page's content -->
    </main>
  </body>
<html>
index.html
* {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
}

html, body {
  height: 100%;
}

body {
  color: #333;
  font-family: sans-serif;
  display: flex;
  flex-direction: column;
}

main {
  flex: 1 0 auto;
}
stiluri.css

Fiecare pagină va avea același antet și subsol, deci este logic să creați un element personalizat pentru fiecare dintre acestea.

Să începem cu antetul.

Definiți un element personalizat

Mai întâi, creați un director numit components și în interiorul acelui director, creați un nou fișier numit header.js cu următorul cod:

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

Este doar un simplu ES5 Class declarându-vă obiceiul Header componentă, cu constructor metoda si speciala super cuvânt cheie. Puteți citi mai multe despre acestea pe MDN.

Prin extinderea genericului HTMLElement clasă, puteți crea orice fel de element doriți. De asemenea, este posibil să extindeți elemente specifice cum ar fi HTMLParagraphElement.

Înregistrați-vă elementul personalizat

Înainte de a putea începe să utilizați elementul personalizat, va trebui să îl înregistrați cu customElements.define() metodă:

class Header extends HTMLElement {
  constructor() {
    super();
  }
}

customElements.define('header-component', Header);
components / header.js

Această metodă ia cel puțin două argumente.

Primul este un DOMString veți folosi atunci când adăugați componenta la pagină, în acest caz, <header-component></header-component>.

Următoarea este clasa componentei pe care ați creat-o mai devreme, aici, Header clasă.

Al treilea argument opțional descrie din care element HTML existent moștenește proprietățile elementului personalizat, de exemplu, {extends: 'p'}. Dar nu vom folosi această caracteristică în acest tutorial.

Folosiți apeluri de apel pentru ciclul de viață pentru a adăuga antetul la pagină

Există patru apeluri speciale pentru ciclul de viață pentru elementele personalizate pe care le putem folosi pentru a adăuga reducerea antetului la pagină: connectedCallback, attributeChangeCallback, disconnectedCallback, și adoptedCallback.

Dintre aceste apeluri de apel, connectedCallback este una dintre cele mai frecvent utilizate. connectedCallback rulează de fiecare dată când elementul dvs. personalizat este inserat în DOM.

Puteți citi mai multe despre celelalte apeluri de apel aici.

Pentru exemplul nostru simplu, connectedCallback este suficient pentru a adăuga un antet la pagină:

class Header extends HTMLElement {
  constructor() {
    super();
  }

  connectedCallback() {
    this.innerHTML = `
      <style>
        nav {
          height: 40px;
          display: flex;
          align-items: center;
          justify-content: center;
          background-color:  #0a0a23;
        }
        
        ul li {
          list-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;
        }
      </style>
      <header>
        <nav>
          <ul>
            <li><a href="https://www.freecodecamp.org/news/reusable-html-components-how-to-reuse-a-header-and-footer-on-a-website/about.html">About</a></li>
            <li><a href="work.html">Work</a></li>
            <li><a href="contact.html">Contact</a></li>
          </ul>
        </nav>
      </header>
    `;
  }
}

customElements.define('header-component', Header);
components / header.js

Apoi în index.html, adaugă components/header.js script și <header-component></header-component> chiar deasupra <main> element:

<!DOCTYPE html>
<html>
  <head>
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.1/css/all.min.css" integrity="sha512-+4zCK9k+qNFUR5X+cKL9EIR+ZOhtIloNl9GIKS57V1MyNsYpYcUrUeQc9vNfzsWfV28IaLL3i96P9sdNyeRssA==" crossorigin="anonymous" />
    <link href="https://www.freecodecamp.org/news/reusable-html-components-how-to-reuse-a-header-and-footer-on-a-website/styles.css" rel="stylesheet" type="text/css" />
    <script src="components/header.js" type="text/javascript" defer></script>
  </head>
  <body>
    <header-component></header-component>
    <main>
      <!-- Your page's content -->
    </main>
  </body>
<html>
index.html

Iar componenta antet reutilizabilă ar trebui redată paginii:

1611046026 89 Componente HTML reutilizabile Cum se reutilizeaza un antet si

Acum, adăugarea unui antet la pagină este la fel de ușor ca adăugarea unui <script> eticheta care indică spre components/header.jsși adăugând <header-component></header-component> oriunde vrei.

Rețineți că, deoarece antetul și stilul său sunt inserate direct în DOM-ul principal, este posibil să îl coafați în styles.css fişier.

Dar dacă vă uitați la stilurile de antet incluse în connectedCallback, sunt destul de generale și pot afecta alte stiluri de pe pagină.

De exemplu, dacă adăugăm următoarea componentă de subsol:

<!DOCTYPE html>
<html>
  <head>
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.1/css/all.min.css" integrity="sha512-+4zCK9k+qNFUR5X+cKL9EIR+ZOhtIloNl9GIKS57V1MyNsYpYcUrUeQc9vNfzsWfV28IaLL3i96P9sdNyeRssA==" crossorigin="anonymous" />
    <link href="https://www.freecodecamp.org/news/reusable-html-components-how-to-reuse-a-header-and-footer-on-a-website/styles.css" rel="stylesheet" type="text/css" />
    <script src="components/header.js" type="text/javascript" defer></script>
    <script src="components/footer.js" type="text/javascript" defer></script>
  </head>
  <body>
    <header-component></header-component>
    <main>
      <!-- Your page's content -->
    </main>
    <footer-component></footer-component>
  </body>
<html>
index.html
class Footer extends HTMLElement {
  constructor() {
    super();
  }

  connectedCallback() {
    this.innerHTML = `
      <style>
        footer {
          height: 60px;
          padding: 0 10px;
          list-style: none;
          display: flex;
          justify-content: space-between;
          align-items: center;
          background-color: #dfdfe2;
        }
        
        ul li {
          list-style: 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="https://www.freecodecamp.org/news/reusable-html-components-how-to-reuse-a-header-and-footer-on-a-website/about.html">About</a></li>
          <li><a href="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://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

Iată cum ar arăta pagina:

1611046026 643 Componente HTML reutilizabile Cum se reutilizeaza un antet si

Stilul din componenta subsolului suprascrie stilul pentru antet, schimbând culoarea legăturilor. Acesta este comportamentul așteptat pentru CSS, dar ar fi frumos dacă stilul fiecărei componente ar fi inclus în componenta respectivă și nu ar afecta alte lucruri de pe pagină.

Ei bine, tocmai acolo strălucește Shadow DOM. Sau nuanțe? Oricum, Shadow DOM poate face asta.

Cum se folosește Shadow DOM cu elemente personalizate

Shadow DOM acționează ca o instanță separată, mai mică, a DOM-ului principal. În loc să acționeze ca o copie a DOM-ului principal, Shadow DOM seamănă mai degrabă cu un subarborel doar pentru elementul dvs. personalizat. Orice element adăugat într-un Shadow DOM, în special stilurile, are ca obiect acel element particularizat.

Într-un fel, este ca și cum ai folosi const și let Decat var.

Să începem prin refactorizarea componentei antet:

const headerTemplate = document.createElement('template');
headerTemplate.innerHTML = `
  <style>
    nav {
      height: 40px;
      display: flex;
      align-items: center;
      justify-content: center;
      background-color:  #0a0a23;
    }
    
    ul li {
      list-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;
    }
  </style>
  <header>
    <nav>
      <ul>
        <li><a href="https://www.freecodecamp.org/news/reusable-html-components-how-to-reuse-a-header-and-footer-on-a-website/about.html">About</a></li>
        <li><a href="work.html">Work</a></li>
        <li><a href="contact.html">Contact</a></li>
      </ul>
    </nav>
  </header>
`;

class Header extends HTMLElement {
  constructor() {
    super();
  }

  connectedCallback() {
    
  }
}

customElements.define('header-component', Header);
components / header.js

Primul lucru pe care trebuie să-l faci este să folosești fișierul .attachShadow() metodă pentru a atașa o rădăcină umbra elementului dvs. personalizat de antet. În connectedCallback, adăugați următorul cod:

...
class Header extends HTMLElement {
  constructor() {
    super();
  }

  connectedCallback() {
    const shadowRoot = this.attachShadow({ mode: 'closed' });
  }
}

customElements.define('header-component', Header);
components / header.js

Observați că îi transmitem un obiect .attachShadow() cu o opțiune, mode: 'closed'. Acest lucru înseamnă doar că DOM-ul umbră al componentei antet este inaccesibil din JavaScript extern.

Dacă doriți să manipulați ulterior componenta antet DOM ulterioară cu JavaScript în afara components/header.js fișier, schimbați opțiunea la mode: 'open'.

În cele din urmă, adăugați shadowRoot la pagina cu .appendChild() metodă:

...

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

Și acum, deoarece stilurile componentei antet sunt încapsulate în Shadow DOM, pagina ar trebui să arate astfel:

1611046026 430 Componente HTML reutilizabile Cum se reutilizeaza un antet si

Iată cum arată codul final în toate fișierele după refactorizarea componentei de subsol:

<!DOCTYPE html>
<html>
  <head>
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.1/css/all.min.css" integrity="sha512-+4zCK9k+qNFUR5X+cKL9EIR+ZOhtIloNl9GIKS57V1MyNsYpYcUrUeQc9vNfzsWfV28IaLL3i96P9sdNyeRssA==" crossorigin="anonymous" />
    <link href="https://www.freecodecamp.org/news/reusable-html-components-how-to-reuse-a-header-and-footer-on-a-website/styles.css" rel="stylesheet" type="text/css" />
    <script src="components/header.js" type="text/javascript" defer></script>
    <script src="components/footer.js" type="text/javascript" defer></script>
  </head>
  <body>
    <header-component></header-component>
    <main>
      <!-- Your page's 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: sans-serif;
  display: flex;
  flex-direction: column;
}

main {
  flex: 1 0 auto;
}
stiluri.css
const headerTemplate = document.createElement('template');
headerTemplate.innerHTML = `
  <style>
    nav {
      height: 40px;
      display: flex;
      align-items: center;
      justify-content: center;
      background-color:  #0a0a23;
    }
    
    ul li {
      list-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;
    }
  </style>
  <header>
    <nav>
      <ul>
        <li><a href="https://www.freecodecamp.org/news/reusable-html-components-how-to-reuse-a-header-and-footer-on-a-website/about.html">About</a></li>
        <li><a href="work.html">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({ mode: 'closed' });
    shadowRoot.appendChild(headerTemplate.content);
  }
}

customElements.define('header-component', Header);
components / header.js
const footerTemplate = document.createElement('template');
footerTemplate.innerHTML = `
  <style>
    footer {
      height: 60px;
      padding: 0 10px;
      list-style: none;
      display: flex;
      flex-shrink: 0;
      justify-content: space-between;
      align-items: center;
      background-color: #dfdfe2;
    }
    
    ul li {
      list-style: 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="https://www.freecodecamp.org/news/reusable-html-components-how-to-reuse-a-header-and-footer-on-a-website/about.html">About</a></li>
      <li><a href="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://www.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({ mode: 'closed' });
    shadowRoot.appendChild(footerTemplate.content);
  }
}

customElements.define('footer-component', Footer);
components / footer.js

În încheiere

Am acoperit multe aici și este posibil să fi decis deja să folosiți React sau Handlebars.js.

Acestea sunt ambele opțiuni grozave!

Totuși, pentru un proiect mai mic în care veți avea nevoie doar de câteva componente reutilizabile, o bibliotecă întreagă sau un limbaj de șablonare ar putea fi exagerat.

Sperăm că acum aveți încrederea în a vă crea propriile componente HTML reutilizabile. Acum ieșiți acolo și creați ceva grozav (și reutilizabil).