influxweb
12/12/2016 - 5:57 PM

How Tabs Should Work (jQuery and Vanilla) https://remysharp.com/2016/12/11/how-tabs-should-work

How Tabs Should Work (jQuery and Vanilla) https://remysharp.com/2016/12/11/how-tabs-should-work

* { margin: 0; padding: 0; }
body { font-family: helvetica; margin: 20px; }

img {
  position: relative;
  border: 3px solid #000;
  height: 200px;
  width: 300px;
  object-fit: cover;
}

ul {
  margin: 0;
  padding: 0;
  list-style: none;
  overflow: hidden;
  margin-bottom: 10px;
}

.panel {
  border: 1px solid #ccc;
  overflow: scroll;
  margin-bottom: 10px;  
}

ul a {
  border: 1px solid #ccc;
  padding: 10px;
  margin-right: 10px;
  float: left;
  display: block;
}

ul a {
  color: #000;
  text-decoration: none;
}

ul.tabs a:hover {
  background: #000;
  border-color: #000;
  color: #fff;
}

ul.tabs a.selected,
ul.tabs a[aria-selected='true'] {
  background: #ccc;
  border-color: #ccc;
  color: #000;
/*   color: #fff;     */
}

.panel {
  padding: 10px;
  width: 550px;
  overflow: hidden;
}

div[aria-hidden='true'] {
  display: none;
}

dl {
  float: right;
  width: 180px;
}

dt {
  font-weight: bold;
}

dd {
  color: #666;
  font-style: italic;
  margin-bottom: 5px;
  margin-left: 10px;
}

p {
  line-height: 200px;
  width: 100px;
}
// adapterd from http://git.io/blingjs
var $ = document.querySelectorAll.bind(document);

Node.prototype.on = window.on = function (name, fn) {
  this.addEventListener(name, fn);
  return this;
};

NodeList.prototype.__proto__ = Array.prototype;

NodeList.prototype.on = NodeList.prototype.addEventListener = function (name, fn) {
  this.forEach(function (el) {
    el.on(name, fn);
  });
  return this;
};

// end of bling.js ----

// a temp value to cache *what* we're about to show
var target = null;

// collect all the tabs
var tabs = $('.tab').on('click', function () {
  target = $(this.hash)[0];
  target.id = '';
  if (location.hash === this.hash) {
    setTimeout(update);
  }
}).map(function (el) {
  el.tabindex = 0;
  return el;
});

// get an array of the panel ids (from the anchor hash)
var targets = tabs.map(function (el) {
  return el.hash;
});

// use those ids to get a jQuery collection of panels
var panels = $(targets.join(',')).map(function (el) {
  // keep a copy of what the original el.id was
  el.dataset.old = el.id;
  return el;
});

function update() {
  if (target) {
    target.id = target.dataset.old;
    target = null;
  }
  
  var hash = window.location.hash;
  if (targets.indexOf(hash) !== -1) {
    return show(hash);
  }
  
  if (!hash) {
    show();
  }
}

function show(id) {
  // if no value was given, let's take the first panel
  if (!id) {
    id = targets[0];
  }
  // remove the selected class from the tabs,
  // and add it back to the one the user selected
  tabs.forEach(function (el) {
    var match = el.hash === id;
    el.classList[match ? 'add' : 'remove']('selected');
    el.setAttribute('aria-selected', match);
  });

  // now hide all the panels, then filter to
  // the one we're interested in, and show it
  panels.forEach(function (el) {
    var match = '#' + el.id === id;
    el.style.display = match ? '' : 'none';
    el.setAttribute('aria-hidden', !match);
  });
}

window.addEventListener('hashchange', update);

// initialise
if (targets.indexOf(window.location.hash) !== -1) {
  update();
} else {
  show();
}
/*global $*/

// a temp value to cache *what* we're about to show
var target = null;

// collect all the tabs
var tabs = $('.tab').on('click', function () {
  console.log('click')
  target = $(this.hash).removeAttr('id');
  if (location.hash === this.hash) {
    setTimeout(update);
  }
}).attr('tabindex', '0');

// get an array of the panel ids (from the anchor hash)
var targets = tabs.map(function () {
  return this.hash;
}).get();

// use those ids to get a jQuery collection of panels
var panels = $(targets.join(',')).each(function () {
  // keep a copy of what the original el.id was
  $(this).data('old-id', this.id);
});

function update() {
  console.log('update')
  if (target) {
    target.attr('id', target.data('old-id'));
    target = null;
  }
  
  var hash = window.location.hash;
  if (targets.indexOf(hash) !== -1) {
    return show(hash);
  }
  
  // NOTE: this was added after the article was written
  // to fix going "back" on the browser nav to an empty state
  if (!hash) {
    show();
  }
}

function show(id) {
  // if no value was given, let's take the first panel
  if (!id) {
    id = targets[0];
  }
  // remove the selected class from the tabs,
  // and add it back to the one the user selected
  tabs.removeClass('selected').attr('aria-selected', 'false').filter(function () {
    return (this.hash === id);
  }).addClass('selected').attr('aria-selected', 'true');

  // now hide all the panels, then filter to
  // the one we're interested in, and show it
  panels.hide().attr('aria-hidden', 'true').filter(id).show().attr('aria-hidden', 'false');
}

window.addEventListener('hashchange', update);

// initialise
if (targets.indexOf(window.location.hash) !== -1) {
  update();
} else {
  show();
}
<!DOCTYPE html>
<html id="top" lang="en" class="js">
<head>
<meta name="viewport" content="width=device-width">
<meta content-type="utf-8" />
<title>Tabs</title>
<script src="https://code.jquery.com/jquery-2.1.4.js"></script>
</head>
<body>

  <ul role="tablist" class="tabs">
    <li><a role="tab" aria-controls="dizzy" class="tab" href="#dizzy">Dizzy</a></li>
    <li><a role="tab" aria-controls="ninja" class="tab" href="#ninja">Ninja</a></li>
    <li><a role="tab" class="tab" aria-controls="missy" href="#missy">Missy</a></li>
  </ul>

  <div role="tabpanel" class="panel" id="dizzy">
    <img src="https://jsbin-user-assets.s3.amazonaws.com/rem/dizzy.jpg" alt="Photo of Dizzy">
    <dl>
      <dt>Name</dt>
      <dd>Dizzy</dd>
      <dt>Colour</dt>
      <dd>Tabby</dd>
      <dt>Super power</dt>
      <dd>Sleeping</dd>
    </dl>
  </div>
  <div role="tabpanel" class="panel" id="ninja">
    <img src="https://jsbin-user-assets.s3.amazonaws.com/rem/ninja.jpg" alt="Photo of Ninja">
    <dl>
      <dt>Name</dt>
      <dd>Lord Ninja of Catville</dd>
      <dt>Colour</dt>
      <dd>Black &amp; White</dd>
      <dt>Super power</dt>
      <dd>Most pathetic meow known to catkind</dd>
    </dl>
  </div>
  <div role="tabpanel" class="panel" id="missy">
    <img src="https://jsbin-user-assets.s3.amazonaws.com/rem/missy.jpg" alt="Photo of Missy">
    <dl>
      <dt>Name</dt>
      <dd>Missy</dd>
      <dt>Colour</dt>
      <dd>Black</dd>
      <dt>Super power</dt>
      <dd>Computer hardware protection</dd>
    </dl>
  </div>
  <small>The following content is demonstrate the effects of a tall page.</small>
  <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Commodi excepturi molestias modi sed harum, assumenda, cupiditate deleniti magni eaque velit quidem voluptate mollitia, consequatur sit sint quos dolorum perspiciatis omnis!</p>
  <a href="#top">top</a>
</body>
</html>