PageSourceSearch

https://www.loechte-gmbh.de/js/_essentials/ruffnav.js

js loechte-gmbh.de collected 2026-10-05 00:56:45 UTC 12,473 bytes, 291 lines download raw bytes

1/**
2 * off-canvas nav
3 * @name ruffnav
4 * @author Dominik Kressler
5 * @version 2.2.6
6 * @thanks Jasmin Reiff
7 * @edit by Malte Zoudlik
8 * @edit by Dario Rieke
9 * @copyright (c) 2018
10 */
11function Ruffnav(options) {
12    this.options = J.merge({
13        toggler: '#navi-toggled',
14        container: '#navigation',
15        navList: '.navi-main',
16        subParent: '.has-sub',
17        subClass: '.sub',
18        backText: false,
19        mobileBreak: 'large',
20        closeOnScroll: true,
21        desktopHover: false,
22        navSplit: false,
23        navAppend: false
24    }, options);
25    this.depth = 0;
26    this.toggler = document.querySelector(this.options.toggler);
27    this.container = document.querySelector(this.options.container);
28    this.navList = this.container.querySelector(this.options.navList)
29    this.sub = this.container.querySelectorAll(this.options.subClass);
30    this.subParent = this.container.querySelectorAll(this.options.subParent);
31    this.html = document.documentElement;
32    this.device = breakpoint(this.options.mobileBreak) ? 'desktop' : 'mobile';
33    this.isOpen = false;
34    this.subToggler = null;
35    this.windowSize = window.innerWidth;
36    this.navSize = 0;
37    this.activePath = '';
38    this.path = window.location.pathname.split('/').filter(function(el) {return el != ''});
39
40    // set back button text by language, or by overwrite
41    if(this.options.backText === false)
42    {
43        var html = document.querySelector('html');
44        this.options.backText = (html.lang == 'de') ? 'Zurück' : 'back';
45    }
46
47    this.backButton = create('li', {
48        'class': 'nav-back',
49        'tabindex': '0'
50    }, '<span>'+this.options.backText+'</span>');
51
52    var self = this;
53    this.init = function() {
54        if(!self.navList) return console.warn(`No element found with class "${self.options.navList}". Ruffnav could't be initialized. Please add the class to your main navigation UL-Element`);
55        // add class for desktop hover
56        self.container.classList.add('first-level'); 
57        if(self.options.desktopHover) self.container.classList.add('hover-navigation');
58
59        // append custom Content to Navigation
60        if(self.options.navAppend) {
61            var itemToAppend = create('li', {'class': 'nav-item appended-item'}, self.options.navAppend);
62            self.navList.appendChild(itemToAppend);
63        }
64
65        self.navSize = self.navList.childNodes.length;
66        // split navigation in half
67
68        // transform Navigation markup
69        [].forEach.call(self.subParent, function(parentItem) {
70            var childNode = parentItem.firstElementChild,
71                categoryLabel = (childNode.dataset && childNode.dataset.categoryLabel) ? childNode.dataset.categoryLabel : childNode.innerHTML; 
72
73            // parent-name attribute for UL's
74            childNode.nextElementSibling.setAttribute('data-parent-name', childNode.innerText);
75                
76            // only clone when Element is a link
77            if(childNode.nodeName !== 'SPAN') {
78                // var cActive = (window.location.origin+window.location.pathname === childNode.href) ? 'active' : '';
79
80                // copy link inside UL's
81                childNode.nextElementSibling.insertBefore(create('li', {
82                    'class': 'nav-item '+childNode.className.trim()
83                }, '<a href="'+childNode.href+'" title="'+childNode.title+'">'+categoryLabel+'</a>'), parentItem.children[1].children[0]);
84                if(self.path.length) {
85                    self.activeClassFallback(childNode);
86                }
87            }
88            // replace link's with span's
89            // parentItem.replaceChild(create('span', {
90            //     'class': 'sub-toggler '+childNode.className.trim(),
91            //     'tabindex': 0,
92            //     'data-href': childNode.href
93            // }, childNode.innerHTML), childNode)
94
95                        // replace link's with span's
96                        if(self.device == "desktop"){
97                            parentItem.replaceChild(create('a', {
98                                'class': 'sub-toggler '+childNode.className.trim(),
99                                'tabindex': 0,
100                                'href': childNode.href
101                            }, childNode.innerHTML), childNode)
102             
103                        }else{
104                          
105                            parentItem.replaceChild(create('span', {
106                                'class': 'sub-toggler '+childNode.className.trim(),
107                                'tabindex': 0,
108                                'data-href': childNode.href
109                            }, childNode.innerHTML), childNode)
110                        }
111        });
112        [].forEach.call(self.sub, function(subItem){
113            // add backbuttons to UL's
114            subItem.insertBefore(self.backButton.cloneNode(true), subItem.childNodes[0]);
115        });
116
117        if(self.options.navSplit) {
118            var elm;
119            var splitList = create('ul', {class: "navi split-list"});
120            for (var i = parseInt(self.navSize / 2); i != splitList.childNodes.length; i) {
121                elm = self.navList.childNodes[i].cloneNode(true);
122                splitList.appendChild(elm)
123                self.navList.childNodes[i].remove();
124            }   
125            self.navList.parentElement.appendChild(splitList);
126        }
127
128        // close on mobile toggle
129        self.toggler.addEventListener('click', self._closeAll);
130
131        // add Listeners for different events
132        self.addListener();
133
134        // add global resize listener
135        self.listenResize();
136
137        return self;
138    }
139    this._open = function(e) {
140        // open event
141        var target, sameLevelCurrents;
142
143        if(e.type === 'mouseenter' && self.device === "mobile") return;
144        if(e.type === 'mouseenter') {
145            target = e.target;
146            target.childNodes[1].classList.add('current');
147            if(self.container.querySelector('.open') != null) self.container.querySelector('.open').classList.remove('open');
148            target.childNodes[1].classList.add('open');
149        } else {
150            target = e.currentTarget;
151            
152            //close other on same level when already opened
153            sameLevelCurrents = target.parentElement.parentElement.querySelector('.current');
154            if(sameLevelCurrents !== null) self.closeRecusive(sameLevelCurrents, false)
155
156            target.nextElementSibling.classList.add('current');
157            if(self.container.querySelector('.open') != null) self.container.querySelector('.open').classList.remove('open');
158            target.nextElementSibling.classList.add('open');
159        }
160        if(!self.isOpen && self.device !== 'mobile') {
161            if(self.options.closeOnScroll) window.addEventListener('scroll', self._closeAll);
162            document.addEventListener('click', self._closeAll);
163            document.addEventListener('keydown', self._closeAll);
164            self.isOpen = true;
165        }
166        self.container.classList.remove('first-level');
167        self.scrollTop();
168    }
169    this._close = function(e) {
170        // close event
171        var currentListTree;
172        
173        if(e.type === 'mouseleave' && self.device === "mobile") return;
174        if(e.type === 'mouseleave') currentListTree = e.currentTarget;
175        else currentListTree = e.currentTarget.parentElement;
176        
177        self.closeRecusive(currentListTree, false);
178        if(self.container.querySelectorAll('ul.current').length === 0) self.isOpen = false;
179        if(self.container.querySelectorAll('ul.current').length === 0) self.container.classList.add('first-level');
180
181        self.scrollTop();
182    }
183    this._closeAll = function(e) {
184        (self.toggler.checked) ? self.html.classList.add('hidden-scroll') : self.html.classList.remove('hidden-scroll');
185        // close all
186        if(e.type === 'scroll') {
187            self.closeRecusive(null, true);
188        } else if(e.type === 'click') {
189            // check if click was not inside navigation
190            if(e.target === self.container || self.container.contains(e.target)) return;
191            else self.closeRecusive(null, true)
192        } else if(e.type === 'keydown') {
193            // check if "enter" was pressed
194            if(e.keyCode === 27) self.closeRecusive(null, true);
195        } else {
196            self.closeRecusive(null, true);
197        }
198        self.removeListeners();        
199        self.isOpen = false
200    }
201    this.closeRecusive = function(firstList, closeAll) {
202        // close all open items
203        if(closeAll) {
204            [].forEach.call(self.container.querySelectorAll('.current'), function(openSubs) {openSubs.classList.remove('current')});
205            if(self.container.querySelector('.open') != null) self.container.querySelector('.open').classList.remove('open');
206            self.container.classList.add('first-level');
207        } else {
208            if(self.container.querySelector('.open') != null) self.container.querySelector('.open').classList.remove('open');
209            [].forEach.call(firstList.querySelectorAll('.current'), function(openSubs){
210                openSubs.classList.remove('current');
211            });
212            firstList.parentNode.parentNode.classList.add('open');
213            firstList.classList.remove('current');
214        }
215    }
216    this.removeListeners = function() {     
217        // remove eventListener for global close
218        window.removeEventListener('scroll', self._closeAll);
219        document.removeEventListener('keydown', self._closeAll);
220        document.removeEventListener('click', self._closeAll);
221    }
222    this.addListener = function() {
223        // add eventListener to subToggle and back
224        self.subToggler = self.container.querySelectorAll('.sub-toggler');
225        self.backButtons = self.container.querySelectorAll('.nav-back');
226        [].forEach.call(self.subToggler, function(subTogglerItem){
227            if(self.options.desktopHover) {
228                subTogglerItem.parentNode.addEventListener('mouseenter', self._open);
229                subTogglerItem.parentNode.addEventListener('mouseleave', self._close);
230            }
231            subTogglerItem.addEventListener('click', self._open);
232        });
233        [].forEach.call(self.backButtons, function(backButtonsItem){
234            backButtonsItem.addEventListener('click', self._close, false);
235        });
236    }
237    this.listenResize = function() {
238        // global resize Event
239        window.onresize =  function(){
240            self.device = breakpoint(self.options.mobileBreak) ? 'desktop' : 'mobile';
241            if(self.device === 'mobile') self.removeListeners();
242            if(self.windowSize != window.innerWidth && self.device === 'desktop') {
243                self.closeRecusive(null, true);
244                self.isOpen = false;
245            }
246            self.windowSize = window.innerWidth;
247        }
248    }
249
250    this.scrollTop = function() {
251        [].forEach.call(self.sub, function(item) {item.scrollTop = 0} )
252        self.container.childNodes[0].scrollTop = 0;
253    }
254
255    function create(type, attr, content) {
256        // create Node element
257        var elm = document.createElement(type);
258        [].forEach.call(Object.entries(attr), function(name){
259            elm.setAttribute(name[0], name[1]);
260        });
261        if(content) elm.innerHTML = content;
262        return elm
263    }
264
265    this.activeClassFallback = function(elem) {
266        if(elem.href === window.location.origin + '/' + this.activePath + this.path[0]+'/') {
267            elem.classList.add('active');
268            if(elem.parentNode.classList.contains(self.options.subParent.substring(1))) elem.parentNode.classList.add('active')
269            if(this.path.length === 1) elem.parentNode.querySelector('[href="'+window.location.origin+'/' + this.activePath + this.path[0]+'/"]').classList.add('active');
270            this.activePath += this.path[0] + '/';
271            this.path.splice(0,1);
272        }
273    }
274
275    this.init();
276}
277if (!Object.entries)
278Object.entries = function( obj ){
279    var ownProps = Object.keys( obj ),
280        i = ownProps.length,
281        resArray = new Array(i); // preallocate the Array
282    while (i--)
283    resArray[i] = [ownProps[i], obj[ownProps[i]]];
284
285    return resArray;
286};
287if (!('remove' in Element.prototype)) { 
288    Element.prototype.remove = function() { 
289        if (this.parentNode) this.parentNode.removeChild(this); 
290    }; 
291}

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.