4
\$\begingroup\$

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>

| improve this question | | | | |
\$\endgroup\$
2
\$\begingroup\$

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.

| improve this answer | | | | |
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.