creator / scripts /menu.js
K1Z3M1112's picture
Upload 8 files
913928f verified
Raw History Blame Contribute Delete
7.81 kB
/**
* Author: Peter Dragicevic [peter@petschko.org]
* Authors-Website: https://petschko.org/
* Date: 19.02.2020
* Time: 19:03
*/
(function() {
'use strict';
var eventAddMethod = window.addEventListener ? 'addEventListener' : 'attachEvent';
var menus = [];
/**
* Returns the current anchor from the URL
*
* @returns {string} - Anchor or empty string if none
*/
function getAnchorFromUrl() {
return window.location.hash.substr(1);
}
/**
* Adds a silent anchor to avoid scrolling
*
* @param {string} anchor - Anchor
*/
function addSilentAnchor(anchor) {
if(history.replaceState)
history.replaceState(null, '', '#' + anchor);
else
window.location.hash = '#' + anchor; // Fallback
}
/**
* Removes a CSS-Class
*
* @param {string} elementClasses - Element CSS-Classes
* @param {string} removeClass - Class to remove
* @returns {string} - New CSS-Classes
*/
function removeCssClass(elementClasses, removeClass) {
var newClassNames = '';
var classes = elementClasses.split(' ');
// Check if element is in array else exit
if(classes.indexOf(removeClass) === -1)
return elementClasses;
for(var i = 0; i < classes.length; i++) {
if(classes[i] !== removeClass)
newClassNames += classes[i] + ' ';
}
return newClassNames.trim();
}
/**
* Adds a CSS-Class, it never adds an existing class twice
*
* @param {string} elementClasses - Element CSS-Classes
* @param {string} addClass - Class to add
* @returns {string} - New CSS-Classes
*/
function addCssClass(elementClasses, addClass) {
var classes = elementClasses.split(' ');
if(classes.indexOf(addClass) !== -1)
return elementClasses;
return (elementClasses + ' ' + addClass).trim();
}
/**
* Check if a the searched Class is within this class-string
*
* @param {string} elementClasses - Element CSS-Classes
* @param {string} searchClass - Class to check
* @returns {boolean} - true if the class exist else false
*/
function hasCssClass(elementClasses, searchClass) {
var classes = elementClasses.split(' ');
return (classes.indexOf(searchClass) !== -1);
}
/**
* Returns the correct function for the action of an Event-Listener
*
* @param {string} action
* @returns {string}
*/
function on(action) {
return window.addEventListener ? action : 'on' + action;
}
/**
* Adds a Event-Listener cross-browser
*
* @param {Element|Node|HTMLElement} element
* @param {string} action
* @param {function} callable
*/
function addEventListener(element, action, callable) {
element[eventAddMethod](on(action), callable);
}
// Menu Actions
/**
* Updates all menus when clicking on anchor link
*/
function updateMenuFromHref() {
var menuHref = document.getElementsByClassName('menu-link');
if(! menuHref)
return;
for(var i = 0; i < menuHref.length; i++) {
addEventListener(menuHref[i], 'click', function(e) {
if(this.href.indexOf('#') === -1)
return;
// e.preventDefault();
var anchor = this.href.split('#')[1];
// Handle all menus
for(var n = 0; n < menus.length; n++) {
menus[n].updateFromAnchor(anchor);
}
});
}
}
/**
* Searches Menus and adds functions on it
*/
function findMenus() {
var menuElements = document.getElementsByClassName('nav-tabs');
if(! menuElements)
return;
for(var i = 0; i < menuElements.length; i++)
menus.push(new Menu(menuElements[i]));
// Add external menu functions
updateMenuFromHref();
}
/**
* Creates a new interactive menu
*
* @param {Element|Node|HTMLElement} menuElement - Menu-List-Element
* @constructor - Menu
*/
function Menu(menuElement) {
this.menuItems = menuElement.getElementsByClassName('nav-link');
this.activeIndex = -1;
this.displayElementIds = [];
/**
* Updates the Active index
*/
this.findActiveIndex = function() {
for(var i = 0; i < this.menuItems.length; i++) {
if(hasCssClass(this.menuItems[i].className, 'active')) {
this.activeIndex = i;
return
}
}
this.activeIndex = -1;
};
/**
* Finds the active Menu-Item when a anchor is given
*/
this.findActiveIndexFromAnchor = function() {
var anchor = getAnchorFromUrl();
for(var i = 0; i < this.menuItems.length; i++) {
if(this.getIdDisplayId(this.menuItems[i]) === anchor) {
this.activeIndex = i;
return;
}
}
this.activeIndex = -1;
};
/**
* Updates the menu with the given anchor if found
*
* @param {string} anchor - Anchor
* @returns {boolean} - Was found and updated
*/
this.updateFromAnchor = function(anchor) {
var found = false;
for(var i = 0; i < this.menuItems.length; i++) {
if(this.getIdDisplayId(this.menuItems[i]) === anchor) {
this.activeIndex = i;
this.updateMenuTo(this.activeIndex);
found = true;
break;
}
}
return found;
};
/**
* Gets the Display Id of an element
*
* @param menuItem - Menu-Item
* @returns {string} - Display ID
*/
this.getIdDisplayId = function(menuItem) {
var anchor = menuItem.getElementsByTagName('a');
return anchor[0].href.split('#')[1];
};
/**
* Removes click ability from the links (To avoid scrolling)
*/
this.preventDefaultLink = function() {
for(var i = 0; i < this.menuItems.length; i++) {
var anchor = this.menuItems[i].getElementsByTagName('a')[0];
addEventListener(anchor, 'click', function(e) {
if(this.href.indexOf('#') === -1)
return;
e.preventDefault();
addSilentAnchor(this.href.split('#')[1]);
});
}
};
/**
* Hides a container with that index
*
* @param {number} index - Index to hide
*/
this.hideContainer = function(index) {
var container = document.getElementById(this.displayElementIds[index]);
if(! container)
return;
container.className = addCssClass(container.className, 'hidden');
};
/**
* Shows a container with that index
*
* @param {number} index - Index to show
*/
this.showContainer = function(index) {
var container = document.getElementById(this.displayElementIds[index]);
if(! container)
return;
container.className = removeCssClass(container.className, 'hidden');
};
/**
* Updates the Menu and container to the index
*
* @param {number} index - New active index
*/
this.updateMenuTo = function(index) {
this.activeIndex = index;
for(var i = 0; i < this.menuItems.length; i++) {
if(i === index) {
this.menuItems[i].className = addCssClass(this.menuItems[i].className, 'active');
this.showContainer(i);
} else {
this.menuItems[i].className = removeCssClass(this.menuItems[i].className, 'active');
this.hideContainer(i);
}
}
};
/**
* Inits the Menu
*/
this.init = function() {
// Check if an anchor is given
if(getAnchorFromUrl())
this.findActiveIndexFromAnchor();
// If nothing is found or given still search normal
if(this.activeIndex === -1)
this.findActiveIndex();
for(var i = 0; i < this.menuItems.length; i++) {
this.displayElementIds.push(this.getIdDisplayId(this.menuItems[i]));
// Add Listener
addEventListener(this.menuItems[i], 'click', function(that, updateIndex) {
return function(e) {
that.updateMenuTo(updateIndex);
}
}(this, i));
if(this.activeIndex !== i)
this.hideContainer(i);
}
if(this.activeIndex === -1)
this.updateMenuTo(0);
else
this.updateMenuTo(this.activeIndex);
this.preventDefaultLink();
};
// Init
this.init();
}
/**
* initialized when page is loaded
*/
function init() {
findMenus();
}
window[window.addEventListener ? 'addEventListener' : 'attachEvent'](
window.addEventListener ? 'load' : 'onload', init, false);
})();