I wrote a script that adapts HTML tabs for different screen resolutions. On a small screen it's an Accordion and on wider ones they're Tabs. The script is for a production site, and I'm not so good in JavaScript yet, so show me any weaknesses.
(function ($) {
// html structure
// div - selector
// // ul
// // // li.class - tabs
$.fn.tabslight = function () {
var classes = {
tabsTrigger: '.tab__trigger',
tabTypeAccordeon: 'tab-type-accordeon',
tabTypeTab: 'tab-type-tabs',
activeClass: 'current',
tabList: 'tab-list',
tabListItem: 'tab-list__item',
tabContent: 'tab-content',
tabContentItem: 'tab-content__item',
tabContent: 'tab__inner'
},
slideDuration = 300, //ms
container = $(this),
contentItems = container.find('.' + classes.tabContentItem),
isAccordeon = true,
tabsUpdate = function() {
var items = $('.' + classes.tabContentItem),
itemsTriger = $('.' + classes.tabListItem),
itemsContent = container.find('.' + classes.tabContent);
isAccordeon = ( $(window).width() <= '768' ? true : false );
if (isAccordeon) {
container.addClass( classes.tabTypeAccordeon ).removeClass( classes.tabTypeTab );
} else {
container.addClass( classes.tabTypeTab ).removeClass( classes.tabTypeAccordeon );
}
items // Set initial state for tabs
.eq(0)
.addClass(classes.activeClass) // Add active class to elements
.siblings() // find all siblings of this elements...
.removeClass(classes.activeClass); // .. and remove active class
itemsTriger // Set initial state for tab triggers
.eq(0)
.addClass(classes.activeClass) // Add active class to elements
.siblings() // find all siblings of this elements...
.removeClass(classes.activeClass); // .. and remove active class
itemsContent.removeAttr('style'); // Remove all previous styles for content (inline display: block/none after sliding animation on accordeon statement)
},
tabsList = function() {
var string ='<ul class="' + classes.tabList + '">';
$(classes.tabsTrigger).each(function(){
var item = this.outerHTML;
string += '<li class="' + classes.tabListItem + '">' + item + '</li>';
});
string += '</ul>';
container.prepend(string);// Prepend list tabs for desktop statemenm
tabsUpdate(); // First run tabs update function.
}(),
triggers = container.find(classes.tabsTrigger);
// Listen for resize, and update tabs type
window.addEventListener('resize', tabsUpdate);
triggers.on('click', function (e) {
e.preventDefault(); // Disable the default action on clicking item
if ( isAccordeon ) { // if accordeon
var item = $(this).parent(),
itemContent = item.find('.' + classes.tabContent),
items = item.siblings();
itemsContent = items.find('.' + classes.tabContent);
if ( ! item.hasClass( classes.activeClass ) ) { // if elem don't has current class
items.removeClass(classes.activeClass);
item.addClass(classes.activeClass);
itemsContent.stop(true, true).slideUp(slideDuration); // hide siblings content
itemContent.stop(true, true).slideDown(slideDuration); // show current content
} else { // if elem has current class
itemContent.stop(true, true).slideUp(slideDuration); // hide current content
item.stop(true, true).removeClass(classes.activeClass); // remove current class
}
} else { // if not accordeon
var itemTrigger = $(this).parent(),
itemTriggerSiblings = itemTrigger.siblings(),
itemTriggerNumber = itemTrigger.index(),
item = container.find('.' + classes.tabContentItem).eq(itemTriggerNumber);
itemContent = item.find('.' + classes.tabContent),
items = item.siblings();
itemsContent = items.find('.' + classes.tabContent);
if ( ! item.hasClass( classes.activeClass ) ) { // if don't has current class
itemTrigger
.add(item) // Add to set current tab content (to perform actions for both: tab and content)
.addClass(classes.activeClass) // Add active class to elements
.siblings() // find all siblings of this elements...
.removeClass(classes.activeClass); // ...and remove from them active class
}
}
});
};
$(document).ready(function(){
// Call the function
$('.tab').tabslight();
});
})(jQuery);
body {
font-family: Arial, "Helvetica Neue", Helvetica, sans-serif;
font-size: 16px; }
.wrap {
margin: 0 auto;
max-width: 1000px; }
.tab {
width: 100%; }
.tab-list, .tab-content {
list-style: none;
padding: 0;
margin: 0; }
.tab-list {
display: none; }
@media screen and (min-width: 768px) {
.tab-list {
display: flex; } }
.tab-list__item {
color: white;
margin-right: 1em;
cursor: pointer;
background-color: #666;
color: #fff;
text-decoration: none;
transition: all 0.5s ease-in; }
.tab-list__item.current, .tab-list__item:hover, .tab-list__item:active {
background-color: #aaa; }
.tab-list__item a {
display: block;
padding: .5em;
color: inherit;
text-decoration: none; }
.tab-content {
border: 2px solid #666; }
.tab-content__item .tab__inner {
display: none; }
.tab-content .tab__trigger {
background-color: #666;
color: #fff;
padding: .5em;
text-decoration: none;
transition: all 0.5s ease-in;
display: none; }
@media screen and (max-width: 767px) {
.tab-content .tab__trigger {
display: block; } }
.tab-content .tab__trigger:hover, .tab-content .tab__trigger:active {
background-color: #aaa; }
.tab-type-tabs .tab-content__item.current .tab__inner {
display: block; }
.tab-type-accordeon .tab-content__item:first-child .tab__inner {
display: block; }
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=0"/>
<title>tab</title>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/normalize/5.0.0/normalize.min.css">
<link rel="stylesheet" href="style.css" class="css">
</head>
<body>
<div class="wrap">
<div class="tab">
<ul class="tab-content">
<li class="tab-content__item tab1">
<a class="tab__trigger" href="#">Таб 1</a>
<div class="tab__inner">
<h2>tab 1</h2>
<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Dicta delectus hic, enim aperiam dolores facilis placeat quaerat dignissimos maxime minus eum, a architecto error accusamus esse debitis temporibus explicabo officia!</p>
</div>
</li>
<li class="tab-content__item tab2">
<a class="tab__trigger" href="#">Таб 2</a>
<div class="tab__inner">
<h2>tab 2</h2>
<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Deleniti eligendi expedita nostrum. Maxime quaerat tempora laboriosam temporibus aspernatur impedit beatae deserunt molestias! Doloribus non adipisci nam amet commodi excepturi ut.</p>
</div>
</li>
<li class="tab-content__item tab3">
<a class="tab__trigger" href="#">Таб 3</a>
<div class="tab__inner">
<h2>tab 3</h2>
<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Reiciendis animi optio quod enim ea magnam alias, quae. Quidem quaerat a tempora! In quas velit, illum aut consequuntur molestiae quidem ducimus.</p>
</div>
</li>
<li class="tab-content__item tab4">
<a class="tab__trigger" href="#">Таб 4</a>
<div class="tab__inner">
<h2>tab 4</h2>
<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Aliquam, a laudantium incidunt odio nihil, corporis veniam totam ex. Amet necessitatibus, esse laudantium eius doloribus ex sunt rem, quaerat consectetur laborum.</p>
</div>
</li>
</ul><!-- .tab-list -->
</div><!-- .tab -->
</div><!-- .wrap -->
<script type="text/javascript" src="https://code.jquery.com/jquery-2.1.4.min.js"></script>
<script type="text/javascript" src="script.js"></script>
</body>
</html>
-
1\$\begingroup\$ I have rolled back Rev 5 → 4. Please don't make major changes after the question has been answered. See What to do when someone answers . \$\endgroup\$200_success– 200_success2016年12月08日 00:27:45 +00:00Commented Dec 8, 2016 at 0:27
1 Answer 1
This is a partial review
Comma operator
Don't use the comma operator. Besides that it is a maintenance hell, and increases the amount of variables that are accidentally dropped in the global namespace, it serves no practical purpose here. On top of that, you even use it outside var
statements, creating something that is even more incomprehensible to review.
Your use of the comma operator also messes with the indentation you are using, making it harder to read your code.
var itemTrigger = $(this).parent(),
itemTriggerSiblings = itemTrigger.siblings(),
itemTriggerNumber = itemTrigger.index(),
item = container.find('.' + classes.tabContentItem).eq(itemTriggerNumber);
itemContent = item.find('.' + classes.tabContent),
items = item.siblings();
itemsContent = items.find('.' + classes.tabContent);
In the code above you have a semicolon after defining all variables. Then have two more statements seperated by a comma, with a semicolon. Then another statement with a semicolon. Why? Just use the semicolon (;
) after each statement.
Naming
Decide on a consistent naming scheme for your classes. It makes little sense to use both the -
and the __
separator in those class names.