Tabs Widget page
Create a tabs widget using your own version of jQuery.
Overview
In this part, we will:
- Create a 
$.fn.tabswidget. 
Slides
Exercise: $.fn.tabs
The problem
Create a progressively enhanced tabs widget. It will be called like:
$("#breeds, #tech").tabs();
Notice that .tabs() can be called on multiple elements. An independent tabs widget should be created
on each element in the collection. The elements in the collection should be <ul> elements with the
following structure:
<ul id="tech">
  <li><a href="#canjs">CanJS</a></li>
  <li><a href="#stealjs">StealJS</a></li>
  <li><a href="#donejs">DoneJS</a></li>
</ul>
<div id="canjs">
  <a href="https://canjs.com">CanJS</a>
</div>
<div id="stealjs">
  <a href="https://stealjs.com">StealJS</a>
</div>
<div id="donejs">
  <a href="https://donejs.com">DoneJS</a>
</div>
The <ul> elements will have <li> children which serve as the buttons. Each <li>
must contain an <a> element. The <a> element’s href attributes reference the
id of a tab element to show when the corresponding <li> button element is
clicked.
For example when this <li> is clicked:
<li><a href="#stealjs">StealJS</a></li>
The following tab element should be shown:
<div id="stealjs">
  <a href="https://stealjs.com">StealJS</a>
</div>
Finally:
- Each 
<ul>should havetabsadded to itsclassName. - Each tab element should have 
tabadded to itsclassName. 
The following CodePen can be used to complete the exercise.
<style>
/* adapted from https://codepen.io/talleyran/pen/XoPLvy */
body {
  font-family: Arial;
  font-size: 18px;
}
.tabs {
  display: flex;
  margin: 1rem 0 0.4rem 0;
  padding: 0;
  position: relative;
}
.tabs li {
  list-style: none;
  padding: 0;
  line-height: 1.3rem;
}
.tabs li a {
  color: #173e4a;
  padding: 1rem 1.7rem;
  margin-left: -1rem;
  text-decoration: none;
  position: relative;
}
.tabs li a:link, .tabs li a:visited, .tabs li a:hover, .tabs li a:active, .tabs li a::selection {
  outline: 0 !important;
  background: none !important;
}
.tabs li:first-child a {
  margin-left: 0.5rem;
}
.tabs li a:before {
  content: "";
  position: absolute;
  top: -0.5rem; left: 0;
  background: #8BC3E8;
  height: 100%;
  width: 100%;
  z-index: -1;
  border-top-left-radius: 1rem;
  border-top-right-radius: 1rem;
  transform: perspective(0.2rem) rotateX(2deg);
  transform-origin: bottom;
  box-shadow: 1px 2px 2px #293140;
}
.tabs li.active {
  z-index: 1;
}
.tabs li.active a {
  cursor: default;
}
.tabs li.active a:before {
  background: #98F1FF;
}
.tabs li:not(.active) a:hover:before {
  background: #A5C3FF;
}
.tab {
  min-width: 30rem;
  background: #98F1FF;
  color: #173e4a;
  border-radius: 0.2rem;
  padding: 1.5rem;
  z-index: 100;
  position: relative;
}
.tab:before {
  display: block;
  position: absolute;
  top: 1px; bottom: 0;
  left: 0; right: 0;
  z-index: -1;
  content: "";
  border-radius: 0.2rem;
  box-shadow: 1px 1px 1px #293140;
}
img {width: 400px;}
</style>
<script src="//bitovi.github.io/academy/static/scripts/my-jquery.js"></script>
<ul id='breeds'>
    <li><a href="#beagles">Beagles</a></li>
    <li><a href="#doberman">Doberman</a></li>
    <li><a href="#boxer">Boxer</a></li>
</ul>
<div id='beagles'>
  Beagle: <img src='//bitovi.github.io/academy/static/img/dom/beagle.jpg'/>
</div>
<div id='doberman'>
  Doberman: <img src='//bitovi.github.io/academy/static/img/dom/doberman.jpg'/>
</div>
<div id='boxer'>
  Boxer: <img src='//bitovi.github.io/academy/static/img/dom/boxer.jpg'/>
</div>
<ul id='tech'>
    <li><a href="#canjs">CanJS</a></li>
    <li><a href="#stealjs">StealJS</a></li>
    <li><a href="#donejs">DoneJS</a></li>
</ul>
<div id='canjs'>
  <a href="https://canjs.com">CanJS</a>
</div>
<div id='stealjs'>
  <a href="https://stealjs.com">StealJS</a>
</div>
<div id='donejs'>
  <a href="https://donejs.com">DoneJS</a>
</div>
<script type="module">
const $ = window.$;
$.fn.tabs = function(){
};
$("#breeds, #tech").tabs()
</script>
What you need to know
An Immediately Invoked Function Expression (IFFE) can be used to prevent variables and functions from being added to the global scope:
(function () { function activate(li) { // DO STUFF return li; } $.fn.tabs = function () { // can use activate activate(); }; })(); // can NOT use activate activate(); //-> throws an errorThe following jQuery functions will be useful:
$("#selector")- Get a collection of elements using a CSS selector.$([element])- Create a jQuery collection from an array of elements.collection.children()- Return a collection of all direct descendants of elements in the source collection.collection.find(selector)- Using a CSS selector, find elements descended from elements in the source collection.collection.addClass(className)- Add a class name to elements in the collection.collection.removeClass(className)- Remove elements in the collection.collection.show()- Show elements in the collection.collection.hide()- Hide elements in the collection.collection.bind(event, handler)- Listen to an event.$.each(collection, cb(i, value) )- Loop through an array-like collection of elements.
Completed Solution
Click to see completed solution
<style>
/* adapted from https://codepen.io/talleyran/pen/XoPLvy */
body {
  font-family: Arial;
  font-size: 18px;
}
.tabs {
  display: flex;
  margin: 1rem 0 0.4rem 0;
  padding: 0;
  position: relative;
}
.tabs li {
  list-style: none;
  padding: 0;
  line-height: 1.3rem;
}
.tabs li a {
  color: #173e4a;
  padding: 1rem 1.7rem;
  margin-left: -1rem;
  text-decoration: none;
  position: relative;
}
.tabs li a:link, .tabs li a:visited, .tabs li a:hover, .tabs li a:active, .tabs li a::selection {
  outline: 0 !important;
  background: none !important;
}
.tabs li:first-child a {
  margin-left: 0.5rem;
}
.tabs li a:before {
  content: "";
  position: absolute;
  top: -0.5rem; left: 0;
  background: #8BC3E8;
  height: 100%;
  width: 100%;
  z-index: -1;
  border-top-left-radius: 1rem;
  border-top-right-radius: 1rem;
  transform: perspective(0.2rem) rotateX(2deg);
  transform-origin: bottom;
  box-shadow: 1px 2px 2px #293140;
}
.tabs li.active {
  z-index: 1;
}
.tabs li.active a {
  cursor: default;
}
.tabs li.active a:before {
  background: #98F1FF;
}
.tabs li:not(.active) a:hover:before {
  background: #A5C3FF;
}
.tab {
  min-width: 30rem;
  background: #98F1FF;
  color: #173e4a;
  border-radius: 0.2rem;
  padding: 1.5rem;
  z-index: 100;
  position: relative;
}
.tab:before {
  display: block;
  position: absolute;
  top: 1px; bottom: 0;
  left: 0; right: 0;
  z-index: -1;
  content: "";
  border-radius: 0.2rem;
  box-shadow: 1px 1px 1px #293140;
}
img {width: 400px;}
</style>
<script src="//bitovi.github.io/academy/static/scripts/my-jquery.js"></script>
<ul id='breeds'>
    <li><a href="#beagles">Beagles</a></li>
    <li><a href="#doberman">Doberman</a></li>
    <li><a href="#boxer">Boxer</a></li>
</ul>
<div id='beagles'>
  Beagle: <img src='//bitovi.github.io/academy/static/img/dom/beagle.jpg'/>
</div>
<div id='doberman'>
  Doberman: <img src='//bitovi.github.io/academy/static/img/dom/doberman.jpg'/>
</div>
<div id='boxer'>
  Boxer: <img src='//bitovi.github.io/academy/static/img/dom/boxer.jpg'/>
</div>
<ul id='tech'>
    <li><a href="#canjs">CanJS</a></li>
    <li><a href="#stealjs">StealJS</a></li>
    <li><a href="#donejs">DoneJS</a></li>
</ul>
<div id='canjs'>
  <a href="https://canjs.com">CanJS</a>
</div>
<div id='stealjs'>
  <a href="https://stealjs.com">StealJS</a>
</div>
<div id='donejs'>
  <a href="https://donejs.com">DoneJS</a>
</div>
<script type="module">
const $ = window.$;
(function(){
  function tabContent(li) {
    return $(li.find("a").attr("href"));
  }
  function activate(li) {
    li.addClass("active");
    tabContent(li).show();
    return li;
  }
  function deactivate(li) {
    li.removeClass("active");
    tabContent(li).hide();
    return li;
  }
  $.fn.tabs = function(){
    this.addClass("tabs");
    return $.each(this, function(i, element) {
      var active,
          $lis = $([ element ]).children();
      $.each($lis, function(i, li) {
        var $li = $([ li ]);
        var $tab = tabContent($li);
        $tab.addClass("tab");
        if (i === 0) {
          active = activate($li);
        } else {
          $tab.hide();
        }
      });
      $lis.bind("click", function(event) {
        deactivate( active );
        active = activate( $([ this ]) );
        event.preventDefault();
      });
    });
  };
})();
$("#breeds, #tech").tabs()
</script>