218 lines
7.7 KiB
JavaScript
218 lines
7.7 KiB
JavaScript
|
;(function ($, window, document, undefined) {
|
||
|
'use strict';
|
||
|
|
||
|
Foundation.libs.tab = {
|
||
|
name : 'tab',
|
||
|
|
||
|
version : '5.4.7',
|
||
|
|
||
|
settings : {
|
||
|
active_class: 'active',
|
||
|
callback : function () {},
|
||
|
deep_linking: false,
|
||
|
scroll_to_content: true,
|
||
|
is_hover: false
|
||
|
},
|
||
|
|
||
|
default_tab_hashes: [],
|
||
|
|
||
|
init : function (scope, method, options) {
|
||
|
var self = this,
|
||
|
S = this.S;
|
||
|
|
||
|
this.bindings(method, options);
|
||
|
this.handle_location_hash_change();
|
||
|
|
||
|
// Store the default active tabs which will be referenced when the
|
||
|
// location hash is absent, as in the case of navigating the tabs and
|
||
|
// returning to the first viewing via the browser Back button.
|
||
|
S('[' + this.attr_name() + '] > .active > a', this.scope).each(function () {
|
||
|
self.default_tab_hashes.push(this.hash);
|
||
|
});
|
||
|
},
|
||
|
|
||
|
events : function () {
|
||
|
var self = this,
|
||
|
S = this.S;
|
||
|
|
||
|
var usual_tab_behavior = function (e) {
|
||
|
var settings = S(this).closest('[' + self.attr_name() +']').data(self.attr_name(true) + '-init');
|
||
|
if (!settings.is_hover || Modernizr.touch) {
|
||
|
e.preventDefault();
|
||
|
e.stopPropagation();
|
||
|
self.toggle_active_tab(S(this).parent());
|
||
|
}
|
||
|
};
|
||
|
|
||
|
S(this.scope)
|
||
|
.off('.tab')
|
||
|
// Click event: tab title
|
||
|
.on('focus.fndtn.tab', '[' + this.attr_name() + '] > * > a', usual_tab_behavior )
|
||
|
.on('click.fndtn.tab', '[' + this.attr_name() + '] > * > a', usual_tab_behavior )
|
||
|
// Hover event: tab title
|
||
|
.on('mouseenter.fndtn.tab', '[' + this.attr_name() + '] > * > a', function (e) {
|
||
|
var settings = S(this).closest('[' + self.attr_name() +']').data(self.attr_name(true) + '-init');
|
||
|
if (settings.is_hover) self.toggle_active_tab(S(this).parent());
|
||
|
});
|
||
|
|
||
|
// Location hash change event
|
||
|
S(window).on('hashchange.fndtn.tab', function (e) {
|
||
|
e.preventDefault();
|
||
|
self.handle_location_hash_change();
|
||
|
});
|
||
|
},
|
||
|
|
||
|
handle_location_hash_change : function () {
|
||
|
|
||
|
var self = this,
|
||
|
S = this.S;
|
||
|
|
||
|
S('[' + this.attr_name() + ']', this.scope).each(function () {
|
||
|
var settings = S(this).data(self.attr_name(true) + '-init');
|
||
|
if (settings.deep_linking) {
|
||
|
// Match the location hash to a label
|
||
|
var hash;
|
||
|
if (settings.scroll_to_content) {
|
||
|
hash = self.scope.location.hash;
|
||
|
} else {
|
||
|
// prefix the hash to prevent anchor scrolling
|
||
|
hash = self.scope.location.hash.replace('fndtn-', '');
|
||
|
}
|
||
|
if (hash != '') {
|
||
|
// Check whether the location hash references a tab content div or
|
||
|
// another element on the page (inside or outside the tab content div)
|
||
|
var hash_element = S(hash);
|
||
|
if (hash_element.hasClass('content') && hash_element.parent().hasClass('tabs-content')) {
|
||
|
// Tab content div
|
||
|
self.toggle_active_tab($('[' + self.attr_name() + '] > * > a[href=' + hash + ']').parent());
|
||
|
} else {
|
||
|
// Not the tab content div. If inside the tab content, find the
|
||
|
// containing tab and toggle it as active.
|
||
|
var hash_tab_container_id = hash_element.closest('.content').attr('id');
|
||
|
if (hash_tab_container_id != undefined) {
|
||
|
self.toggle_active_tab($('[' + self.attr_name() + '] > * > a[href=#' + hash_tab_container_id + ']').parent(), hash);
|
||
|
}
|
||
|
}
|
||
|
} else {
|
||
|
// Reference the default tab hashes which were initialized in the init function
|
||
|
for (var ind = 0; ind < self.default_tab_hashes.length; ind++) {
|
||
|
self.toggle_active_tab($('[' + self.attr_name() + '] > * > a[href=' + self.default_tab_hashes[ind] + ']').parent());
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
});
|
||
|
},
|
||
|
|
||
|
toggle_active_tab: function (tab, location_hash) {
|
||
|
var S = this.S,
|
||
|
tabs = tab.closest('[' + this.attr_name() + ']'),
|
||
|
tab_link = tab.find('a'),
|
||
|
anchor = tab.children('a').first(),
|
||
|
target_hash = '#' + anchor.attr('href').split('#')[1],
|
||
|
target = S(target_hash),
|
||
|
siblings = tab.siblings(),
|
||
|
settings = tabs.data(this.attr_name(true) + '-init'),
|
||
|
interpret_keyup_action = function(e) {
|
||
|
// Light modification of Heydon Pickering's Practical ARIA Examples: http://heydonworks.com/practical_aria_examples/js/a11y.js
|
||
|
|
||
|
// define current, previous and next (possible) tabs
|
||
|
|
||
|
var $original = $(this);
|
||
|
var $prev = $(this).parents('li').prev().children('[role="tab"]');
|
||
|
var $next = $(this).parents('li').next().children('[role="tab"]');
|
||
|
var $target;
|
||
|
|
||
|
// find the direction (prev or next)
|
||
|
|
||
|
switch (e.keyCode) {
|
||
|
case 37:
|
||
|
$target = $prev;
|
||
|
break;
|
||
|
case 39:
|
||
|
$target = $next;
|
||
|
break;
|
||
|
default:
|
||
|
$target = false
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
if ($target.length) {
|
||
|
$original.attr({
|
||
|
'tabindex' : '-1',
|
||
|
'aria-selected' : null
|
||
|
});
|
||
|
$target.attr({
|
||
|
'tabindex' : '0',
|
||
|
'aria-selected' : true
|
||
|
}).focus();
|
||
|
}
|
||
|
|
||
|
// Hide panels
|
||
|
|
||
|
$('[role="tabpanel"]')
|
||
|
.attr('aria-hidden', 'true');
|
||
|
|
||
|
// Show panel which corresponds to target
|
||
|
|
||
|
$('#' + $(document.activeElement).attr('href').substring(1))
|
||
|
.attr('aria-hidden', null);
|
||
|
|
||
|
};
|
||
|
|
||
|
// allow usage of data-tab-content attribute instead of href
|
||
|
if (S(this).data(this.data_attr('tab-content'))) {
|
||
|
target_hash = '#' + S(this).data(this.data_attr('tab-content')).split('#')[1];
|
||
|
target = S(target_hash);
|
||
|
}
|
||
|
|
||
|
if (settings.deep_linking) {
|
||
|
|
||
|
if (settings.scroll_to_content) {
|
||
|
// retain current hash to scroll to content
|
||
|
window.location.hash = location_hash || target_hash;
|
||
|
if (location_hash == undefined || location_hash == target_hash) {
|
||
|
tab.parent()[0].scrollIntoView();
|
||
|
} else {
|
||
|
S(target_hash)[0].scrollIntoView();
|
||
|
}
|
||
|
} else {
|
||
|
// prefix the hashes so that the browser doesn't scroll down
|
||
|
if (location_hash != undefined) {
|
||
|
window.location.hash = 'fndtn-' + location_hash.replace('#', '');
|
||
|
} else {
|
||
|
window.location.hash = 'fndtn-' + target_hash.replace('#', '');
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// WARNING: The activation and deactivation of the tab content must
|
||
|
// occur after the deep linking in order to properly refresh the browser
|
||
|
// window (notably in Chrome).
|
||
|
// Clean up multiple attr instances to done once
|
||
|
tab.addClass(settings.active_class).triggerHandler('opened');
|
||
|
tab_link.attr({"aria-selected": "true", tabindex: 0});
|
||
|
siblings.removeClass(settings.active_class)
|
||
|
siblings.find('a').attr({"aria-selected": "false", tabindex: -1});
|
||
|
target.siblings().removeClass(settings.active_class).attr({"aria-hidden": "true", tabindex: -1});
|
||
|
target.addClass(settings.active_class).attr('aria-hidden', 'false').removeAttr("tabindex");
|
||
|
settings.callback(tab);
|
||
|
target.triggerHandler('toggled', [tab]);
|
||
|
tabs.triggerHandler('toggled', [target]);
|
||
|
|
||
|
tab_link.off('keydown').on('keydown', interpret_keyup_action );
|
||
|
},
|
||
|
|
||
|
data_attr: function (str) {
|
||
|
if (this.namespace.length > 0) {
|
||
|
return this.namespace + '-' + str;
|
||
|
}
|
||
|
|
||
|
return str;
|
||
|
},
|
||
|
|
||
|
off : function () {},
|
||
|
|
||
|
reflow : function () {}
|
||
|
};
|
||
|
}(jQuery, window, window.document));
|