From be80f4bf5f6169dcaa7f536b0ff5a3c7ae1534c6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=92=D0=B0=D1=81=D0=B8=D0=BB=D0=B8=D0=B9?= Date: Wed, 13 Jan 2021 22:46:28 +0300 Subject: [PATCH 1/2] Better optioning Adds options for custom class names and a new possible type for "el" option --- js/src/tabs.js | 167 ++++++++++++++++++++++++++++--------------------- 1 file changed, 97 insertions(+), 70 deletions(-) diff --git a/js/src/tabs.js b/js/src/tabs.js index 6466563..a06fdea 100644 --- a/js/src/tabs.js +++ b/js/src/tabs.js @@ -1,84 +1,111 @@ (function() { - 'use strict'; - - /** - * tabs - * - * @description The Tabs component. - * @param {Object} options The options hash - */ - var tabs = function(options) { - - var el = document.querySelector(options.el); - var tabNavigationLinks = el.querySelectorAll(options.tabNavigationLinks); - var tabContentContainers = el.querySelectorAll(options.tabContentContainers); - var activeIndex = 0; - var initCalled = false; + 'use strict'; /** - * init + * tabs * - * @description Initializes the component by removing the no-js class from - * the component, and attaching event listeners to each of the nav items. - * Returns nothing. + * @description The Tabs component. + * @param {Object} options The options hash */ - var init = function() { - if (!initCalled) { - initCalled = true; - el.classList.remove('no-js'); - - for (var i = 0; i < tabNavigationLinks.length; i++) { - var link = tabNavigationLinks[i]; - handleClick(link, i); + var tabs = function(options) { + + var el = options.hasOwnProperty('el') ? parseElement(options.el) : false; + if (el === false) { + console.error('[Responsive Tabs] Option "el" is not defined'); + return; } - } - }; + else if (!el) { + console.error('[Responsive Tabs] Failed to find an element of selector:', options.el); + return; + } + var tabNavigationLinks = el.querySelectorAll(options.tabNavigationLinks); + var tabContentContainers = el.querySelectorAll(options.tabContentContainers); + var activeClass = options.hasOwnProperty('activeClass') ? options.activeClass : 'is-active'; + var activeClassLink = options.hasOwnProperty('activeClassLink') ? options.activeClassLink : activeClass; + var activeClassContainer = options.hasOwnProperty('activeClassContainer') ? options.activeClassContainer : activeClass; + var activeIndex = 0; + var initCalled = false; - /** - * handleClick - * - * @description Handles click event listeners on each of the links in the - * tab navigation. Returns nothing. - * @param {HTMLElement} link The link to listen for events on - * @param {Number} index The index of that link - */ - var handleClick = function(link, index) { - link.addEventListener('click', function(e) { - e.preventDefault(); - goToTab(index); - }); - }; + /** + * init + * + * @description Initializes the component by removing the no-js class from + * the component, and attaching event listeners to each of the nav items. + * Returns nothing. + */ + var init = function() { + if (!initCalled) { + initCalled = true; + el.classList.remove('no-js'); + + for (var i = 0; i < tabNavigationLinks.length; i++) { + var link = tabNavigationLinks[i]; + handleClick(link, i); + } + } + }; + + /** + * parseElement + * + * @description Parses option that can be of different types + * @param el {Element|string} + * @returns {Element} + */ + function parseElement(el) { + if (el instanceof Element) { + return el; + } + else if (typeof el === 'string') { + return document.querySelector(el); + } + } + + /** + * handleClick + * + * @description Handles click event listeners on each of the links in the + * tab navigation. Returns nothing. + * @param {HTMLElement} link The link to listen for events on + * @param {Number} index The index of that link + */ + var handleClick = function(link, index) { + link.addEventListener('click', function(e) { + e.preventDefault(); + goToTab(index); + }); + }; + + /** + * goToTab + * + * @description Goes to a specific tab based on index. Returns nothing. + * @param {Number} index The index of the tab to go to + */ + var goToTab = function(index) { + if (index !== activeIndex && index >= 0 && index <= tabNavigationLinks.length) { + tabNavigationLinks[activeIndex].classList.remove(activeClassLink); + tabNavigationLinks[index].classList.add(activeClassLink); + tabContentContainers[activeIndex].classList.remove(activeClassContainer); + tabContentContainers[index].classList.add(activeClassContainer); + activeIndex = index; + } + }; + + /** + * Returns init and goToTab + */ + return { + init: init, + goToTab: goToTab + }; - /** - * goToTab - * - * @description Goes to a specific tab based on index. Returns nothing. - * @param {Number} index The index of the tab to go to - */ - var goToTab = function(index) { - if (index !== activeIndex && index >= 0 && index <= tabNavigationLinks.length) { - tabNavigationLinks[activeIndex].classList.remove('is-active'); - tabNavigationLinks[index].classList.add('is-active'); - tabContentContainers[activeIndex].classList.remove('is-active'); - tabContentContainers[index].classList.add('is-active'); - activeIndex = index; - } }; /** - * Returns init and goToTab + * Attach to global namespace */ - return { - init: init, - goToTab: goToTab - }; - - }; - - /** - * Attach to global namespace - */ - window.tabs = tabs; + window.tabs = tabs; -})(); \ No newline at end of file +})(); From f9efc437d8c92e1213c83698862997db94af6436 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=92=D0=B0=D1=81=D0=B8=D0=BB=D0=B8=D0=B9?= Date: Wed, 13 Jan 2021 23:07:54 +0300 Subject: [PATCH 2/2] (doc) Update README according to the previous commit --- README.md | 62 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 62 insertions(+) diff --git a/README.md b/README.md index 5415410..2006664 100644 --- a/README.md +++ b/README.md @@ -36,6 +36,8 @@ Include the link to the JS component and instantiate like this: ``` +You can also use `window.tabs` to create a Reponsive Tabs instance. + To initialize it, make sure you run the `init` method: ```javascript @@ -48,6 +50,66 @@ To go to a specific tab index manually after instantiation, you can use the `goT myTabs.goToTab(3); ``` +## Advanced usage + +### Using custom element + +You can also pass an already existing element as an option: + +```javascript +var tabsContainer = document.querySelector('#tabs'); + +var myTabs = window.tabs({ + el: tabsContainer, + tabNavigationLinks: '.c-tabs-nav__link', + tabContentContainers: '.c-tab' +}); +``` + +### Multiple instances + +Using previous example enables the option to create multiple Responsive Tabs instances of same class on the same page: + +```javascript +document.querySelectorAll('.c-tabs').forEach(function (el) { + var myTabs = window.tabs({ + el: el, + tabNavigationLinks: '.tab', + tabContentContainers: '.tab-content' + }); + myTabs.init(); +}); +``` + +### Custom classes for active elements + +You can also define your own classes for active tab links and contents to match the desired CSS syntax. + +Renaming default `is-active` class using `activeClass` option: + +```javascript +var myTabs = tabs({ + el: '#tabs', + tabNavigationLinks: '.c-tabs-nav__link', + tabContentContainers: '.c-tab', + + activeClass: 'c-tab--active' +}); +``` + +Assigning different classes for tab links and tab contents using `activeClassLink` and `activeClassContainer` options: + +```javascript +var myTabs = tabs({ + el: '#tabs', + tabNavigationLinks: '.c-tabs-nav__link', + tabContentContainers: '.c-tab', + + activeClassLink: 'c-tabs-nav__link--active', + activeClassContainer: 'c-tab--active' +}); +``` + ## No JS Fallback Some simple CSS is provided for a no-js solution. If JS is enabled, the `no-js` class gets removed during instantiation and all works well!