Use mutation observer for easier toggline with JS

This commit is contained in:
Cory LaViska
2017-08-29 17:53:51 -04:00
parent b9f1b32b2a
commit 7c2a0fcc95
4 changed files with 77 additions and 37 deletions

4
dist/shoelace.js vendored
View File

@@ -5,11 +5,11 @@
Released under the MIT license
Source: https://github.com/claviska/shoelace-css
*/
!function(){"use strict";if("undefined"==typeof jQuery&&"undefined"==typeof Zepto)throw new Error("Shoelace dropdowns require either jQuery or Zepto.");("function"==typeof jQuery?jQuery:Zepto)(function(e){e(document).on("click",function(t){var i,o,r;if(e(t.target).closest(".dropdown-trigger")){if(i=e(t.target).closest(".dropdown"),r=t.target,e(".dropdown.active").not(i).removeClass("active").trigger("hide"),e(r).is(".disabled, :disabled"))return;e(i).toggleClass("active").trigger(e(i).is(".active")?"show":"hide")}else e(t.target).closest(".dropdown-menu").length&&(i=e(t.target).closest(".dropdown"),(o=e(t.target).closest("a").get(0))&&!e(o).is(".disabled")&&e(i).trigger("select",o),t.preventDefault()),e(".dropdown.active").removeClass("active").trigger("hide")}).on("keydown",function(t){27===t.keyCode&&e(".dropdown.active").removeClass("active").trigger("hide")})})}(),/*!
!function(){"use strict";if("undefined"==typeof jQuery&&"undefined"==typeof Zepto)throw new Error("Shoelace dropdowns require either jQuery or Zepto.");("function"==typeof jQuery?jQuery:Zepto)(function(e){e(document).on("click",function(t){var r,i,o;if(e(t.target).closest(".dropdown-trigger")){if(r=e(t.target).closest(".dropdown"),o=t.target,e(".dropdown.active").not(r).removeClass("active").trigger("hide"),e(o).is(".disabled, :disabled"))return;e(r).toggleClass("active").trigger(e(r).is(".active")?"show":"hide")}else e(t.target).closest(".dropdown-menu").length&&(r=e(t.target).closest(".dropdown"),(i=e(t.target).closest("a").get(0))&&!e(i).is(".disabled")&&e(r).trigger("select",i),t.preventDefault()),e(".dropdown.active").removeClass("active").trigger("hide")}).on("keydown",function(t){27===t.keyCode&&e(".dropdown.active").removeClass("active").trigger("hide")})})}(),/*!
Shoelace.css tabs 1.0.0-beta20
(c) A Beautiful Site, LLC
Released under the MIT license
Source: https://github.com/claviska/shoelace-css
*/
function(){"use strict";if("undefined"==typeof jQuery&&"undefined"==typeof Zepto)throw new Error("Shoelace tabs require either jQuery or Zepto.");(window.jQuery||window.Zepto)(function(e){e(document).on("click",".tabs-nav a",function(t){var i=e(this).closest(".tabs"),o=this,r=e(i).find(o.hash).get(0);t.preventDefault(),o.hash&&!e(o).is(".disabled")&&(e(o).siblings().removeClass("active"),e(o).addClass("active"),e(i).find(".tabs-pane.active").not(r).each(function(){e(this).removeClass("active"),e(i).trigger("hide",this)}),r&&!e(r).is(".active")&&(e(r).addClass("active"),e(i).trigger("show",r)))})})}();
function(){"use strict";if("undefined"==typeof jQuery&&"undefined"==typeof Zepto)throw new Error("Shoelace tabs require either jQuery or Zepto.");(window.jQuery||window.Zepto)(function(e){new MutationObserver(function(t){t.forEach(function(t){if("attributes"===t.type&&"class"===t.attributeName){var r=e(t.target).get(0),i=e(t.target).closest(".tabs").get(0),o=e(t.target).closest(".tabs-nav").get(0),a="A"===r.tagName?e(i).find(r.hash):null;if(!e(r).is("a")||!i||!o)return;if(e(r).is(".disabled.active"))return void e(r).removeClass("active");e(r).is(".active")?(e(r).siblings(".active").removeClass("active"),e(a).addClass("active"),e(i).trigger("show",a)):(e(a).removeClass("active"),e(i).trigger("hide",a))}})}).observe(document.body,{attributes:!0,attributeFilter:["class"],attributeOldValue:!0,subtree:!0}),e(document).on("click",".tabs-nav a",function(t){var r=this;t.preventDefault(),e(r).is(".disabled")||e(r).addClass("active")})})}();

View File

@@ -53,8 +53,8 @@
<div id="content">
<h2 id="tabs">Tabs</h2>
<p>Tab sets can be created using the markup below. By default, Shoelace renders tabs as pills because they respond better than traditional tabs when rendered on smaller screens.</p>
<p>Note the class names used for the main container, the tab navs, and the tab panes. Also note that each tab links to its respective tab panes <code>id</code>.</p>
<p>To disable a tab, add <code>disabled</code> to the appropriate tab nav.</p>
<p>Note the class names used for the main container, the tabs, and the tab panes. Also note that each tab links to its respective tab panes <code>id</code>.</p>
<p>For initial rendering, make sure the appropriate tab and tab pane have the <code>active</code> class.</p>
<pre><code class="lang-html">&lt;div class=&quot;tabs&quot;&gt;
&lt;nav class=&quot;tabs-nav&quot;&gt;
&lt;a href=&quot;#tab-1&quot; class=&quot;active&quot;&gt;Tab 1&lt;/a&gt;
@@ -113,7 +113,7 @@
</div>
<h3 id="vertical-tabs">Vertical Tabs</h3>
<p>Tabs can be made vertical when used along with the <a href="grid-system.html">grid system</a>.</p>
<p>Tabs can be made vertical when used with the <a href="grid-system.html">grid system</a>.</p>
<pre><code class="lang-html">&lt;div class=&quot;tabs&quot;&gt;
&lt;div class=&quot;row&quot;&gt;
&lt;div class=&quot;col-4&quot;&gt;
@@ -172,9 +172,9 @@
</div>
</div>
<h3 id="events">Events</h3>
<p>Tabs require <code>shoelace.js</code> to make them interactive. You dont need to initialize them. Simply include the script and everything “just works.”</p>
<p>There is no JavaScript API. Shoelaces philosophy believes that custom components should act like native components as much as possible. You can, however, listen for various events:</p>
<h3 id="interactivity">Interactivity</h3>
<p>Tabs require <code>shoelace.js</code> for interactivity. You dont need to initialize anything. Just include the script and everything “just works.”</p>
<p>There is no JavaScript API. Shoelaces philosophy believes that custom components should act like native components as much as possible. You can, however, listen for various events.</p>
<ul>
<li><code>show</code> Fires when a tab is shown. The second callback argument is a reference to the respective tab pane.</li>
<li><code>hide</code> Fires when a tab is hidden. The second callback argument is a reference to the respective tab pane.</li>
@@ -188,6 +188,10 @@
console.log(&#39;hide&#39;, event.target, tabPane);
});
</code></pre>
<p>To activate a tab programmatically, just add the <code>active</code> class to it. We use a <a href="https://developer.mozilla.org/en-US/docs/Web/API/MutationObserver">mutation observer</a> to remove the active class on other tabs and to show/hide the appropriate tab panes automatically.</p>
<pre><code class="lang-javascript">$(&#39;#tab-id&#39;).addClass(&#39;active&#39;);
</code></pre>
<p>To disable a tab, add the <code>disabled</code> class to the appropriate tab.</p>
</div>
</div>

View File

@@ -8,9 +8,9 @@ description: Add tabs to your app with the tabs component.
Tab sets can be created using the markup below. By default, Shoelace renders tabs as pills because they respond better than traditional tabs when rendered on smaller screens.
Note the class names used for the main container, the tab navs, and the tab panes. Also note that each tab links to its respective tab panes `id`.
Note the class names used for the main container, the tabs, and the tab panes. Also note that each tab links to its respective tab panes `id`.
To disable a tab, add `disabled` to the appropriate tab nav.
For initial rendering, make sure the appropriate tab and tab pane have the `active` class.
```html
<div class="tabs">
@@ -73,7 +73,7 @@ To disable a tab, add `disabled` to the appropriate tab nav.
### Vertical Tabs
Tabs can be made vertical when used along with the [grid system](grid-system.html).
Tabs can be made vertical when used with the [grid system](grid-system.html).
```html
<div class="tabs">
@@ -135,11 +135,11 @@ Tabs can be made vertical when used along with the [grid system](grid-system.htm
</div>
</div>
### Events
### Interactivity
Tabs require `shoelace.js` to make them interactive. You dont need to initialize them. Simply include the script and everything “just works.”
Tabs require `shoelace.js` for interactivity. You dont need to initialize anything. Just include the script and everything “just works.”
There is no JavaScript API. Shoelaces philosophy believes that custom components should act like native components as much as possible. You can, however, listen for various events:
There is no JavaScript API. Shoelaces philosophy believes that custom components should act like native components as much as possible. You can, however, listen for various events.
- `show`  Fires when a tab is shown. The second callback argument is a reference to the respective tab pane.
- `hide`  Fires when a tab is hidden. The second callback argument is a reference to the respective tab pane.
@@ -155,3 +155,11 @@ $('#my-tabs')
console.log('hide', event.target, tabPane);
});
```
To activate a tab programmatically, just add the `active` class to it. We use a [mutation observer](https://developer.mozilla.org/en-US/docs/Web/API/MutationObserver) to remove the active class on other tabs and to show/hide the appropriate tab panes automatically.
```javascript
$('#tab-id').addClass('active');
```
To disable a tab, add the `disabled` class to the appropriate tab.

View File

@@ -23,7 +23,7 @@
// Tabs not toggling?
// - Make sure you've loaded jQuery or Zepto before this script
// - Make sure your tabs are structured properly per the docs
// - Make sure your tab navs and tab panes have the correct IDs
// - Make sure your tab navs and tab panes have the correct href and id attributes
//
(function() {
/* eslint-env browser, jquery */
@@ -35,34 +35,62 @@
}
(window.jQuery || window.Zepto)(function($) {
// Watch for mutations on tabs and show the appropriate tab pane
var observer = new MutationObserver(function(mutations) {
mutations.forEach(function(mutation) {
// Only observe class changes
if(mutation.type === 'attributes' && mutation.attributeName === 'class') {
var tab = $(mutation.target).get(0);
var tabs = $(mutation.target).closest('.tabs').get(0);
var tabsNav = $(mutation.target).closest('.tabs-nav').get(0);
var tabPane = tab.tagName === 'A' ? $(tabs).find(tab.hash) : null;
// The mutation must be on a tab
if(!$(tab).is('a') || !tabs || !tabsNav) return;
// Disabled tabs can't receive the active class, so we just remove it
if($(tab).is('.disabled.active')) {
$(tab).removeClass('active');
return;
}
// Toggle the tab pane based on the tab's active state
if($(tab).is('.active')) {
// Remove the active class from other tabs
$(tab).siblings('.active').removeClass('active');
// Show the selected tab
$(tabPane).addClass('active');
$(tabs).trigger('show', tabPane);
} else {
// Hide the previously selected tab
$(tabPane).removeClass('active');
$(tabs).trigger('hide', tabPane);
}
}
});
});
// Observe the body so we can handle dynamically created elements
observer.observe(document.body, {
attributes: true,
attributeFilter: ['class'],
attributeOldValue: true,
subtree: true
});
// Watch for clicks on tabs
$(document).on('click', '.tabs-nav a', function(event) {
var tabs = $(this).closest('.tabs');
var tabNav = this;
var selectedPane = $(tabs).find(tabNav.hash).get(0);
var tab = this;
event.preventDefault();
// Ignore tabs without an href or with the "disabled" class
if(!tabNav.hash || $(tabNav).is('.disabled')) {
return;
}
// Ignore disabled tabs
if($(tab).is('.disabled')) return;
// Make the selected tab active
$(tabNav).siblings().removeClass('active');
$(tabNav).addClass('active');
// Hide active tab panes that aren't getting selected
$(tabs).find('.tabs-pane.active').not(selectedPane).each(function() {
$(this).removeClass('active');
$(tabs).trigger('hide', this);
});
// Show the selected tab pane
if(selectedPane && !$(selectedPane).is('.active')) {
$(selectedPane).addClass('active');
$(tabs).trigger('show', selectedPane);
}
// Make the selected tab active. No need to worry about showing the tab pane or making other
// tabs inactive because the mutation observer will handle that.
$(tab).addClass('active');
});
});
})();