priority-nav 1.0.12 → 1.0.14
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +22 -22
- package/README.md +106 -96
- package/demo/css/main.css +155 -155
- package/demo/css/priority-nav-core.css +38 -38
- package/demo/images/src/svg/arrow-down.svg +11 -11
- package/demo/images/src/svg/github-mark.svg +11 -11
- package/demo/images/src/svg/knob.svg +19 -19
- package/demo/index.html +143 -143
- package/demo/js/classList.js +69 -69
- package/demo/js/html5shiv.min.js +3 -3
- package/demo/js/priority-nav.js +666 -666
- package/demo/scss/base/_base.scss +13 -13
- package/demo/scss/base/_boxsizing.scss +13 -13
- package/demo/scss/base/_typography.scss +37 -37
- package/demo/scss/components/_github-links.scss +11 -11
- package/demo/scss/components/_logo.scss +14 -14
- package/demo/scss/components/_nav.scss +118 -118
- package/demo/scss/components/_social.scss +8 -8
- package/demo/scss/layouts/_header.scss +20 -20
- package/demo/scss/main.scss +42 -42
- package/demo/scss/pages/_home.scss +29 -29
- package/demo/scss/utils/_easings.scss +38 -38
- package/demo/scss/utils/_helpers.scss +92 -92
- package/demo/scss/utils/_triangles.scss +26 -26
- package/demo/scss/utils/_variables.scss +29 -29
- package/demo/scss/vendors/_normalize.scss +424 -424
- package/dist/priority-nav-core.css +9 -9
- package/dist/priority-nav.js +719 -724
- package/dist/priority-nav.min.js +3 -2
- package/package.json +46 -46
package/demo/js/priority-nav.js
CHANGED
@@ -1,667 +1,667 @@
|
|
1
|
-
(function (root, factory) {
|
2
|
-
if (typeof define === "function" && define.amd) {
|
3
|
-
define("priorityNav", factory(root));
|
4
|
-
} else if (typeof exports === "object") {
|
5
|
-
module.exports = factory(root);
|
6
|
-
} else {
|
7
|
-
root.priorityNav = factory(root);
|
8
|
-
}
|
9
|
-
})(window || this, function (root) {
|
10
|
-
|
11
|
-
"use strict";
|
12
|
-
|
13
|
-
/**
|
14
|
-
* Variables
|
15
|
-
*/
|
16
|
-
var priorityNav = {}; // Object for public APIs
|
17
|
-
var breaks = []; // Object to store instances with breakpoints where the instances menu item"s didin"t fit.
|
18
|
-
var supports = !!document.querySelector && !!root.addEventListener // Feature test
|
19
|
-
var settings = {};
|
20
|
-
var instance = 0;
|
21
|
-
var count = 0;
|
22
|
-
var mainNavWrapper, totalWidth, restWidth, mainNav, navDropdown, navDropdownToggle, dropDownWidth, toggleWrapper;
|
23
|
-
var viewportWidth = 0;
|
24
|
-
|
25
|
-
/**
|
26
|
-
* Default settings
|
27
|
-
* @type {{initClass: string, navDropdown: string, navDropdownToggle: string, mainNavWrapper: string, moved: Function, movedBack: Function}}
|
28
|
-
*/
|
29
|
-
var defaults = {
|
30
|
-
initClass: "js-priorityNav", // Class that will be printed on html element to allow conditional css styling.
|
31
|
-
mainNavWrapper: "nav", // mainnav wrapper selector (must be direct parent from mainNav)
|
32
|
-
mainNav: "ul", // mainnav selector. (must be inline-block)
|
33
|
-
navDropdown: "nav__dropdown", // class used for the dropdown.
|
34
|
-
navDropdownToggle: "nav__dropdown-toggle", // class used for the dropdown toggle.
|
35
|
-
navDropdownLabel: "more", // Text that is used for the dropdown toggle.
|
36
|
-
navDropdownBreakpointLabel: "menu", //button label for navDropdownToggle when the breakPoint is reached.
|
37
|
-
breakPoint: 500, //amount of pixels when all menu items should be moved to dropdown to simulate a mobile menu
|
38
|
-
throttleDelay: 50, // this will throttle the calculating logic on resize because i'm a responsible dev.
|
39
|
-
offsetPixels: 0, // increase to decrease the time it takes to move an item.
|
40
|
-
count: true, // prints the amount of items are moved to the attribute data-count to style with css counter.
|
41
|
-
|
42
|
-
//Callbacks
|
43
|
-
moved: function () {
|
44
|
-
},
|
45
|
-
movedBack: function () {
|
46
|
-
}
|
47
|
-
};
|
48
|
-
|
49
|
-
|
50
|
-
/**
|
51
|
-
* A simple forEach() implementation for Arrays, Objects and NodeLists
|
52
|
-
* @private
|
53
|
-
* @param {Array|Object|NodeList} collection Collection of items to iterate
|
54
|
-
* @param {Function} callback Callback function for each iteration
|
55
|
-
* @param {Array|Object|NodeList} scope Object/NodeList/Array that forEach is iterating over (aka `this`)
|
56
|
-
*/
|
57
|
-
var forEach = function (collection, callback, scope) {
|
58
|
-
if (Object.prototype.toString.call(collection) === "[object Object]") {
|
59
|
-
for (var prop in collection) {
|
60
|
-
if (Object.prototype.hasOwnProperty.call(collection, prop)) {
|
61
|
-
callback.call(scope, collection[prop], prop, collection);
|
62
|
-
}
|
63
|
-
}
|
64
|
-
} else {
|
65
|
-
for (var i = 0, len = collection.length; i < len; i++) {
|
66
|
-
callback.call(scope, collection[i], i, collection);
|
67
|
-
}
|
68
|
-
}
|
69
|
-
};
|
70
|
-
|
71
|
-
|
72
|
-
/**
|
73
|
-
* Get the closest matching element up the DOM tree
|
74
|
-
* @param {Element} elem Starting element
|
75
|
-
* @param {String} selector Selector to match against (class, ID, or data attribute)
|
76
|
-
* @return {Boolean|Element} Returns false if not match found
|
77
|
-
*/
|
78
|
-
var getClosest = function (elem, selector) {
|
79
|
-
var firstChar = selector.charAt(0);
|
80
|
-
for (; elem && elem !== document; elem = elem.parentNode) {
|
81
|
-
if (firstChar === ".") {
|
82
|
-
if (elem.classList.contains(selector.substr(1))) {
|
83
|
-
return elem;
|
84
|
-
}
|
85
|
-
} else if (firstChar === "#") {
|
86
|
-
if (elem.id === selector.substr(1)) {
|
87
|
-
return elem;
|
88
|
-
}
|
89
|
-
} else if (firstChar === "[") {
|
90
|
-
if (elem.hasAttribute(selector.substr(1, selector.length - 2))) {
|
91
|
-
return elem;
|
92
|
-
}
|
93
|
-
}
|
94
|
-
}
|
95
|
-
return false;
|
96
|
-
};
|
97
|
-
|
98
|
-
|
99
|
-
/**
|
100
|
-
* Merge defaults with user options
|
101
|
-
* @private
|
102
|
-
* @param {Object} defaults Default settings
|
103
|
-
* @param {Object} options User options
|
104
|
-
* @returns {Object} Merged values of defaults and options
|
105
|
-
*/
|
106
|
-
var extend = function (defaults, options) {
|
107
|
-
var extended = {};
|
108
|
-
forEach(defaults, function (value, prop) {
|
109
|
-
extended[prop] = defaults[prop];
|
110
|
-
});
|
111
|
-
forEach(options, function (value, prop) {
|
112
|
-
extended[prop] = options[prop];
|
113
|
-
});
|
114
|
-
return extended;
|
115
|
-
};
|
116
|
-
|
117
|
-
|
118
|
-
/**
|
119
|
-
* Debounced resize to throttle execution
|
120
|
-
* @param func
|
121
|
-
* @param wait
|
122
|
-
* @param immediate
|
123
|
-
* @returns {Function}
|
124
|
-
*/
|
125
|
-
function debounce(func, wait, immediate) {
|
126
|
-
var timeout;
|
127
|
-
return function () {
|
128
|
-
var context = this, args = arguments;
|
129
|
-
var later = function () {
|
130
|
-
timeout = null;
|
131
|
-
if (!immediate) func.apply(context, args);
|
132
|
-
};
|
133
|
-
var callNow = immediate && !timeout;
|
134
|
-
clearTimeout(timeout);
|
135
|
-
timeout = setTimeout(later, wait);
|
136
|
-
if (callNow) func.apply(context, args);
|
137
|
-
};
|
138
|
-
}
|
139
|
-
|
140
|
-
|
141
|
-
/**
|
142
|
-
* Toggle class on element
|
143
|
-
* @param el
|
144
|
-
* @param className
|
145
|
-
*/
|
146
|
-
var toggleClass = function (el, className) {
|
147
|
-
if (el.classList) {
|
148
|
-
el.classList.toggle(className);
|
149
|
-
} else {
|
150
|
-
var classes = el.className.split(" ");
|
151
|
-
var existingIndex = classes.indexOf(className);
|
152
|
-
|
153
|
-
if (existingIndex >= 0)
|
154
|
-
classes.splice(existingIndex, 1); else
|
155
|
-
classes.push(className);
|
156
|
-
|
157
|
-
el.className = classes.join(" ");
|
158
|
-
}
|
159
|
-
};
|
160
|
-
|
161
|
-
|
162
|
-
/**
|
163
|
-
* Check if dropdown menu is already on page before creating it
|
164
|
-
* @param mainNavWrapper
|
165
|
-
*/
|
166
|
-
var prepareHtml = function (_this, settings) {
|
167
|
-
|
168
|
-
/**
|
169
|
-
* Create dropdow menu
|
170
|
-
* @type {HTMLElement}
|
171
|
-
*/
|
172
|
-
toggleWrapper = document.createElement("span");
|
173
|
-
navDropdown = document.createElement("ul");
|
174
|
-
navDropdownToggle = document.createElement("button");
|
175
|
-
|
176
|
-
/**
|
177
|
-
* Set label for dropdown toggle
|
178
|
-
* @type {string}
|
179
|
-
*/
|
180
|
-
navDropdownToggle.innerHTML = settings.navDropdownLabel;
|
181
|
-
|
182
|
-
|
183
|
-
/**
|
184
|
-
* Move elements to the right spot
|
185
|
-
*/
|
186
|
-
_this.insertAfter(toggleWrapper, _this.querySelector(mainNav));
|
187
|
-
|
188
|
-
toggleWrapper.appendChild(navDropdown);
|
189
|
-
toggleWrapper.appendChild(navDropdownToggle);
|
190
|
-
|
191
|
-
/**
|
192
|
-
* Add classes so we can target elements
|
193
|
-
*/
|
194
|
-
navDropdown.classList.add(settings.navDropdown);
|
195
|
-
navDropdown.classList.add("priority-nav__dropdown");
|
196
|
-
|
197
|
-
navDropdownToggle.classList.add(settings.navDropdownToggle);
|
198
|
-
navDropdownToggle.classList.add("priority-nav__dropdown-toggle");
|
199
|
-
|
200
|
-
toggleWrapper.classList.add(settings.navDropdown+"-wrapper");
|
201
|
-
toggleWrapper.classList.add("priority-nav__wrapper");
|
202
|
-
|
203
|
-
_this.classList.add("priority-nav");
|
204
|
-
};
|
205
|
-
|
206
|
-
|
207
|
-
/**
|
208
|
-
* Get innerwidth without padding
|
209
|
-
* @param element
|
210
|
-
* @returns {number}
|
211
|
-
*/
|
212
|
-
var getElementContentWidth = function(element) {
|
213
|
-
var styles = window.getComputedStyle(element);
|
214
|
-
var padding = parseFloat(styles.paddingLeft) +
|
215
|
-
parseFloat(styles.paddingRight);
|
216
|
-
|
217
|
-
return element.clientWidth - padding;
|
218
|
-
};
|
219
|
-
|
220
|
-
|
221
|
-
/**
|
222
|
-
* Get viewport size
|
223
|
-
* @returns {{width: number, height: number}}
|
224
|
-
*/
|
225
|
-
var viewportSize = function() {
|
226
|
-
var doc = document, w = window;
|
227
|
-
var docEl = (doc.compatMode && doc.compatMode === "CSS1Compat")?
|
228
|
-
doc.documentElement: doc.body;
|
229
|
-
|
230
|
-
var width = docEl.clientWidth;
|
231
|
-
var height = docEl.clientHeight;
|
232
|
-
|
233
|
-
// mobile zoomed in?
|
234
|
-
if ( w.innerWidth && width > w.innerWidth ) {
|
235
|
-
width = w.innerWidth;
|
236
|
-
height = w.innerHeight;
|
237
|
-
}
|
238
|
-
|
239
|
-
return {width: width, height: height};
|
240
|
-
};
|
241
|
-
|
242
|
-
|
243
|
-
/**
|
244
|
-
* Get width
|
245
|
-
* @param elem
|
246
|
-
* @returns {number}
|
247
|
-
*/
|
248
|
-
var calculateWidths = function (_this) {
|
249
|
-
totalWidth = getElementContentWidth(_this);
|
250
|
-
//Check if parent is the navwrapper before calculating its width
|
251
|
-
if (_this.querySelector(navDropdown).parentNode === _this) {
|
252
|
-
dropDownWidth = _this.querySelector(navDropdown).offsetWidth;
|
253
|
-
} else {
|
254
|
-
dropDownWidth = 0;
|
255
|
-
}
|
256
|
-
restWidth = getChildrenWidth(_this) + settings.offsetPixels;
|
257
|
-
viewportWidth = viewportSize().width;
|
258
|
-
};
|
259
|
-
|
260
|
-
|
261
|
-
/**
|
262
|
-
* Move item to array
|
263
|
-
* @param item
|
264
|
-
*/
|
265
|
-
priorityNav.doesItFit = function (_this, settings) {
|
266
|
-
|
267
|
-
/**
|
268
|
-
* Check if it is the first run
|
269
|
-
*/
|
270
|
-
var delay = _this.getAttribute("instance") === 0 ? delay : settings.throttleDelay;
|
271
|
-
|
272
|
-
/**
|
273
|
-
* Increase instance
|
274
|
-
*/
|
275
|
-
instance++;
|
276
|
-
|
277
|
-
/**
|
278
|
-
* Debounced execution of the main logic
|
279
|
-
*/
|
280
|
-
(debounce(function () {
|
281
|
-
|
282
|
-
/**
|
283
|
-
* Get the current element"s instance
|
284
|
-
* @type {string}
|
285
|
-
*/
|
286
|
-
var identifier = _this.getAttribute("instance");
|
287
|
-
|
288
|
-
/**
|
289
|
-
* Update width
|
290
|
-
*/
|
291
|
-
calculateWidths(_this);
|
292
|
-
|
293
|
-
/**
|
294
|
-
* Keep executing until all menu items that are overflowing are moved
|
295
|
-
*/
|
296
|
-
while (totalWidth <= restWidth && _this.querySelector(mainNav).children.length > 0 || viewportWidth < settings.breakPoint && _this.querySelector(mainNav).children.length > 0) {
|
297
|
-
//move item to dropdown
|
298
|
-
priorityNav.toDropdown(_this, identifier);
|
299
|
-
//recalculate widths
|
300
|
-
calculateWidths(_this, identifier);
|
301
|
-
//update dropdownToggle label
|
302
|
-
if(viewportWidth < settings.breakPoint) updateLabel(_this, identifier, settings.navDropdownBreakpointLabel);
|
303
|
-
}
|
304
|
-
|
305
|
-
/**
|
306
|
-
* Keep executing until all menu items that are able to move back are moved
|
307
|
-
*/
|
308
|
-
while (totalWidth >= breaks[identifier][breaks[identifier].length - 1] && viewportWidth > settings.breakPoint) {
|
309
|
-
//move item to menu
|
310
|
-
priorityNav.toMenu(_this, identifier);
|
311
|
-
//update dropdownToggle label
|
312
|
-
if(viewportWidth > settings.breakPoint) updateLabel(_this, identifier, settings.navDropdownLabel);
|
313
|
-
}
|
314
|
-
|
315
|
-
/**
|
316
|
-
* If there are no items in dropdown hide dropdown
|
317
|
-
*/
|
318
|
-
if (breaks[identifier].length < 1) {
|
319
|
-
_this.querySelector(navDropdown).classList.remove("show");
|
320
|
-
//show navDropdownLabel
|
321
|
-
updateLabel(_this, identifier, settings.navDropdownLabel);
|
322
|
-
}
|
323
|
-
|
324
|
-
/**
|
325
|
-
* If there are no items in menu
|
326
|
-
*/
|
327
|
-
if (_this.querySelector(mainNav).children.length < 1){
|
328
|
-
//show navDropdownBreakpointLabel
|
329
|
-
updateLabel(_this, identifier, settings.navDropdownBreakpointLabel);
|
330
|
-
}
|
331
|
-
|
332
|
-
/**
|
333
|
-
* Check if we need to show toggle menu button
|
334
|
-
*/
|
335
|
-
showToggle(_this, identifier);
|
336
|
-
|
337
|
-
}, delay ))();
|
338
|
-
};
|
339
|
-
|
340
|
-
|
341
|
-
/**
|
342
|
-
* Show/hide toggle button
|
343
|
-
*/
|
344
|
-
var showToggle = function (_this, identifier) {
|
345
|
-
if (breaks[identifier].length < 1) {
|
346
|
-
_this.querySelector(navDropdownToggle).classList.add("priority-nav-is-hidden");
|
347
|
-
_this.querySelector(navDropdownToggle).classList.remove("priority-nav-is-visible");
|
348
|
-
_this.classList.remove("priority-nav-has-dropdown");
|
349
|
-
} else {
|
350
|
-
_this.querySelector(navDropdownToggle).classList.add("priority-nav-is-visible");
|
351
|
-
_this.querySelector(navDropdownToggle).classList.remove("priority-nav-is-hidden");
|
352
|
-
_this.classList.add("priority-nav-has-dropdown");
|
353
|
-
}
|
354
|
-
};
|
355
|
-
|
356
|
-
|
357
|
-
/**
|
358
|
-
* Update count on dropdown toggle button
|
359
|
-
*/
|
360
|
-
var updateCount = function (_this, identifier) {
|
361
|
-
_this.querySelector(navDropdownToggle).setAttribute("priorityNav-count", breaks[identifier].length);
|
362
|
-
};
|
363
|
-
|
364
|
-
var updateLabel = function(_this, identifier, label){
|
365
|
-
_this.querySelector(navDropdownToggle).innerHTML = label;
|
366
|
-
};
|
367
|
-
|
368
|
-
|
369
|
-
/**
|
370
|
-
* Move item to dropdown
|
371
|
-
*/
|
372
|
-
priorityNav.toDropdown = function (_this, identifier) {
|
373
|
-
|
374
|
-
|
375
|
-
/**
|
376
|
-
* move last child of navigation menu to dropdown
|
377
|
-
*/
|
378
|
-
if (_this.querySelector(navDropdown).firstChild && _this.querySelector(mainNav).children.length > 0) {
|
379
|
-
_this.querySelector(navDropdown).insertBefore(_this.querySelector(mainNav).lastElementChild, _this.querySelector(navDropdown).firstChild);
|
380
|
-
} else if (_this.querySelector(mainNav).children.length > 0) {
|
381
|
-
_this.querySelector(navDropdown).appendChild(_this.querySelector(mainNav).lastElementChild);
|
382
|
-
}
|
383
|
-
|
384
|
-
/**
|
385
|
-
* store breakpoints
|
386
|
-
*/
|
387
|
-
breaks[identifier].push(restWidth);
|
388
|
-
|
389
|
-
/**
|
390
|
-
* check if we need to show toggle menu button
|
391
|
-
*/
|
392
|
-
showToggle(_this, identifier);
|
393
|
-
|
394
|
-
/**
|
395
|
-
* update count on dropdown toggle button
|
396
|
-
*/
|
397
|
-
if (_this.querySelector(mainNav).children.length > 0 && settings.count) {
|
398
|
-
updateCount(_this, identifier);
|
399
|
-
}
|
400
|
-
|
401
|
-
/**
|
402
|
-
* If item has been moved to dropdown trigger the callback
|
403
|
-
*/
|
404
|
-
settings.moved();
|
405
|
-
};
|
406
|
-
|
407
|
-
|
408
|
-
/**
|
409
|
-
* Move item to menu
|
410
|
-
*/
|
411
|
-
priorityNav.toMenu = function (_this, identifier) {
|
412
|
-
|
413
|
-
/**
|
414
|
-
* move last child of navigation menu to dropdown
|
415
|
-
*/
|
416
|
-
if (_this.querySelector(navDropdown).children.length > 0) _this.querySelector(mainNav).appendChild(_this.querySelector(navDropdown).firstElementChild);
|
417
|
-
|
418
|
-
/**
|
419
|
-
* remove last breakpoint
|
420
|
-
*/
|
421
|
-
breaks[identifier].pop();
|
422
|
-
|
423
|
-
/**
|
424
|
-
* Check if we need to show toggle menu button
|
425
|
-
*/
|
426
|
-
showToggle(_this, identifier);
|
427
|
-
|
428
|
-
/**
|
429
|
-
* update count on dropdown toggle button
|
430
|
-
*/
|
431
|
-
if (_this.querySelector(mainNav).children.length > 0 && settings.count) {
|
432
|
-
updateCount(_this, identifier);
|
433
|
-
}
|
434
|
-
|
435
|
-
/**
|
436
|
-
* If item has been moved back to the main menu trigger the callback
|
437
|
-
*/
|
438
|
-
settings.movedBack();
|
439
|
-
};
|
440
|
-
|
441
|
-
|
442
|
-
/**
|
443
|
-
* Count width of children and return the value
|
444
|
-
* @param e
|
445
|
-
*/
|
446
|
-
var getChildrenWidth = function (e) {
|
447
|
-
var children = e.childNodes;
|
448
|
-
var sum = 0;
|
449
|
-
for (var i = 0; i < children.length; i++) {
|
450
|
-
if (children[i].nodeType !== 3) {
|
451
|
-
if(!isNaN(children[i].offsetWidth)){
|
452
|
-
sum += children[i].offsetWidth;
|
453
|
-
}
|
454
|
-
|
455
|
-
}
|
456
|
-
}
|
457
|
-
return sum;
|
458
|
-
};
|
459
|
-
|
460
|
-
|
461
|
-
/**
|
462
|
-
* Bind eventlisteners
|
463
|
-
*/
|
464
|
-
var listeners = function (_this, settings) {
|
465
|
-
|
466
|
-
// Check if an item needs to move
|
467
|
-
if(window.attachEvent) {
|
468
|
-
window.attachEvent("onresize", function() {
|
469
|
-
if(priorityNav.doesItFit)priorityNav.doesItFit(_this, settings);
|
470
|
-
});
|
471
|
-
}
|
472
|
-
else if(window.addEventListener) {
|
473
|
-
window.addEventListener("resize", function() {
|
474
|
-
if(priorityNav.doesItFit)priorityNav.doesItFit(_this, settings);
|
475
|
-
}, true);
|
476
|
-
}
|
477
|
-
|
478
|
-
// Toggle dropdown
|
479
|
-
_this.querySelector(navDropdownToggle).addEventListener("click", function () {
|
480
|
-
toggleClass(_this.querySelector(navDropdown), "show");
|
481
|
-
toggleClass(this, "is-open");
|
482
|
-
toggleClass(_this, "is-open");
|
483
|
-
});
|
484
|
-
|
485
|
-
/*
|
486
|
-
* Remove when clicked outside dropdown
|
487
|
-
*/
|
488
|
-
document.addEventListener("click", function (event) {
|
489
|
-
if (!getClosest(event.target, settings.navDropdown) && event.target !== _this.querySelector(navDropdownToggle)) {
|
490
|
-
_this.querySelector(navDropdown).classList.remove("show");
|
491
|
-
_this.querySelector(navDropdownToggle).classList.remove("is-open");
|
492
|
-
_this.classList.remove("is-open");
|
493
|
-
}
|
494
|
-
});
|
495
|
-
|
496
|
-
/**
|
497
|
-
* Remove when escape key is pressed
|
498
|
-
*/
|
499
|
-
document.onkeydown = function (evt) {
|
500
|
-
evt = evt || window.event;
|
501
|
-
if (evt.keyCode === 27) {
|
502
|
-
navDropdown.classList.remove("show");
|
503
|
-
navDropdownToggle.classList.remove("is-open");
|
504
|
-
mainNavWrapper.classList.remove("is-open");
|
505
|
-
}
|
506
|
-
};
|
507
|
-
};
|
508
|
-
|
509
|
-
|
510
|
-
/**
|
511
|
-
* Remove function
|
512
|
-
*/
|
513
|
-
Element.prototype.remove = function() {
|
514
|
-
this.parentElement.removeChild(this);
|
515
|
-
};
|
516
|
-
|
517
|
-
/*global HTMLCollection */
|
518
|
-
NodeList.prototype.remove = HTMLCollection.prototype.remove = function() {
|
519
|
-
for(var i = 0, len = this.length; i < len; i++) {
|
520
|
-
if(this[i] && this[i].parentElement) {
|
521
|
-
this[i].parentElement.removeChild(this[i]);
|
522
|
-
}
|
523
|
-
}
|
524
|
-
};
|
525
|
-
|
526
|
-
|
527
|
-
/**
|
528
|
-
* Destroy the current initialization.
|
529
|
-
* @public
|
530
|
-
*/
|
531
|
-
priorityNav.destroy = function () {
|
532
|
-
// If plugin isn"t already initialized, stop
|
533
|
-
if (!settings) return;
|
534
|
-
// Remove feedback class
|
535
|
-
document.documentElement.classList.remove(settings.initClass);
|
536
|
-
// Remove toggle
|
537
|
-
toggleWrapper.remove();
|
538
|
-
// Remove settings
|
539
|
-
settings = null;
|
540
|
-
delete priorityNav.init;
|
541
|
-
delete priorityNav.doesItFit;
|
542
|
-
};
|
543
|
-
|
544
|
-
|
545
|
-
/**
|
546
|
-
* insertAfter function
|
547
|
-
* @param n
|
548
|
-
* @param r
|
549
|
-
*/
|
550
|
-
if (supports && typeof Node !== 'undefined'){
|
551
|
-
Node.prototype.insertAfter = function(n,r) {this.insertBefore(n,r.nextSibling);};
|
552
|
-
}
|
553
|
-
|
554
|
-
|
555
|
-
/**
|
556
|
-
* Initialize Plugin
|
557
|
-
* @public
|
558
|
-
* @param {Object} options User settings
|
559
|
-
*/
|
560
|
-
priorityNav.init = function (options) {
|
561
|
-
|
562
|
-
|
563
|
-
// Feature test.
|
564
|
-
if (!supports && typeof Node === 'undefined'){
|
565
|
-
console.warn("This browser doesn't support priorityNav");
|
566
|
-
return;
|
567
|
-
}
|
568
|
-
|
569
|
-
/**
|
570
|
-
* Merge user options with defaults
|
571
|
-
* @type {Object}
|
572
|
-
*/
|
573
|
-
settings = extend(defaults, options || {});
|
574
|
-
|
575
|
-
/**
|
576
|
-
* Store nodes
|
577
|
-
* @type {NodeList}
|
578
|
-
*/
|
579
|
-
var elements = document.querySelectorAll(settings.mainNavWrapper);
|
580
|
-
|
581
|
-
/**
|
582
|
-
* Loop over every instance and reference _this
|
583
|
-
*/
|
584
|
-
forEach(elements, function(_this){
|
585
|
-
|
586
|
-
/**
|
587
|
-
* Create breaks array
|
588
|
-
* @type {number}
|
589
|
-
*/
|
590
|
-
breaks[count] = [];
|
591
|
-
|
592
|
-
/**
|
593
|
-
* Set the instance number as data attribute
|
594
|
-
*/
|
595
|
-
_this.setAttribute("instance", count++);
|
596
|
-
|
597
|
-
/**
|
598
|
-
* Store the wrapper element
|
599
|
-
*/
|
600
|
-
mainNavWrapper = _this;
|
601
|
-
if (!mainNavWrapper) {
|
602
|
-
console.warn("couldn't find the specified mainNavWrapper element");
|
603
|
-
return;
|
604
|
-
}
|
605
|
-
|
606
|
-
/**
|
607
|
-
* Store the menu elementStore the menu element
|
608
|
-
*/
|
609
|
-
mainNav = settings.mainNav;
|
610
|
-
if (!_this.querySelector(mainNav)) {
|
611
|
-
console.warn("couldn't find the specified mainNav element");
|
612
|
-
return;
|
613
|
-
}
|
614
|
-
|
615
|
-
/**
|
616
|
-
* Check if we need to create the dropdown elements
|
617
|
-
*/
|
618
|
-
prepareHtml(_this, settings);
|
619
|
-
|
620
|
-
/**
|
621
|
-
* Store the dropdown element
|
622
|
-
*/
|
623
|
-
navDropdown = "."+settings.navDropdown;
|
624
|
-
if (!_this.querySelector(navDropdown)) {
|
625
|
-
console.warn("couldn't find the specified navDropdown element");
|
626
|
-
return;
|
627
|
-
}
|
628
|
-
|
629
|
-
/**
|
630
|
-
* Store the dropdown toggle element
|
631
|
-
*/
|
632
|
-
navDropdownToggle = "."+settings.navDropdownToggle;
|
633
|
-
if (!_this.querySelector(navDropdownToggle)) {
|
634
|
-
console.warn("couldn't find the specified navDropdownToggle element");
|
635
|
-
return;
|
636
|
-
}
|
637
|
-
|
638
|
-
/**
|
639
|
-
* Event listeners
|
640
|
-
*/
|
641
|
-
listeners(_this, settings);
|
642
|
-
|
643
|
-
/**
|
644
|
-
* Start first check
|
645
|
-
*/
|
646
|
-
priorityNav.doesItFit(_this, settings);
|
647
|
-
|
648
|
-
});
|
649
|
-
|
650
|
-
/**
|
651
|
-
* Count amount of instances
|
652
|
-
*/
|
653
|
-
instance++;
|
654
|
-
|
655
|
-
/**
|
656
|
-
* Add class to HTML element to activate conditional CSS
|
657
|
-
*/
|
658
|
-
document.documentElement.classList.add(settings.initClass);
|
659
|
-
};
|
660
|
-
|
661
|
-
|
662
|
-
/**
|
663
|
-
* Public APIs
|
664
|
-
*/
|
665
|
-
return priorityNav;
|
666
|
-
|
1
|
+
(function (root, factory) {
|
2
|
+
if (typeof define === "function" && define.amd) {
|
3
|
+
define("priorityNav", factory(root));
|
4
|
+
} else if (typeof exports === "object") {
|
5
|
+
module.exports = factory(root);
|
6
|
+
} else {
|
7
|
+
root.priorityNav = factory(root);
|
8
|
+
}
|
9
|
+
})(window || this, function (root) {
|
10
|
+
|
11
|
+
"use strict";
|
12
|
+
|
13
|
+
/**
|
14
|
+
* Variables
|
15
|
+
*/
|
16
|
+
var priorityNav = {}; // Object for public APIs
|
17
|
+
var breaks = []; // Object to store instances with breakpoints where the instances menu item"s didin"t fit.
|
18
|
+
var supports = !!document.querySelector && !!root.addEventListener // Feature test
|
19
|
+
var settings = {};
|
20
|
+
var instance = 0;
|
21
|
+
var count = 0;
|
22
|
+
var mainNavWrapper, totalWidth, restWidth, mainNav, navDropdown, navDropdownToggle, dropDownWidth, toggleWrapper;
|
23
|
+
var viewportWidth = 0;
|
24
|
+
|
25
|
+
/**
|
26
|
+
* Default settings
|
27
|
+
* @type {{initClass: string, navDropdown: string, navDropdownToggle: string, mainNavWrapper: string, moved: Function, movedBack: Function}}
|
28
|
+
*/
|
29
|
+
var defaults = {
|
30
|
+
initClass: "js-priorityNav", // Class that will be printed on html element to allow conditional css styling.
|
31
|
+
mainNavWrapper: "nav", // mainnav wrapper selector (must be direct parent from mainNav)
|
32
|
+
mainNav: "ul", // mainnav selector. (must be inline-block)
|
33
|
+
navDropdown: "nav__dropdown", // class used for the dropdown.
|
34
|
+
navDropdownToggle: "nav__dropdown-toggle", // class used for the dropdown toggle.
|
35
|
+
navDropdownLabel: "more", // Text that is used for the dropdown toggle.
|
36
|
+
navDropdownBreakpointLabel: "menu", //button label for navDropdownToggle when the breakPoint is reached.
|
37
|
+
breakPoint: 500, //amount of pixels when all menu items should be moved to dropdown to simulate a mobile menu
|
38
|
+
throttleDelay: 50, // this will throttle the calculating logic on resize because i'm a responsible dev.
|
39
|
+
offsetPixels: 0, // increase to decrease the time it takes to move an item.
|
40
|
+
count: true, // prints the amount of items are moved to the attribute data-count to style with css counter.
|
41
|
+
|
42
|
+
//Callbacks
|
43
|
+
moved: function () {
|
44
|
+
},
|
45
|
+
movedBack: function () {
|
46
|
+
}
|
47
|
+
};
|
48
|
+
|
49
|
+
|
50
|
+
/**
|
51
|
+
* A simple forEach() implementation for Arrays, Objects and NodeLists
|
52
|
+
* @private
|
53
|
+
* @param {Array|Object|NodeList} collection Collection of items to iterate
|
54
|
+
* @param {Function} callback Callback function for each iteration
|
55
|
+
* @param {Array|Object|NodeList} scope Object/NodeList/Array that forEach is iterating over (aka `this`)
|
56
|
+
*/
|
57
|
+
var forEach = function (collection, callback, scope) {
|
58
|
+
if (Object.prototype.toString.call(collection) === "[object Object]") {
|
59
|
+
for (var prop in collection) {
|
60
|
+
if (Object.prototype.hasOwnProperty.call(collection, prop)) {
|
61
|
+
callback.call(scope, collection[prop], prop, collection);
|
62
|
+
}
|
63
|
+
}
|
64
|
+
} else {
|
65
|
+
for (var i = 0, len = collection.length; i < len; i++) {
|
66
|
+
callback.call(scope, collection[i], i, collection);
|
67
|
+
}
|
68
|
+
}
|
69
|
+
};
|
70
|
+
|
71
|
+
|
72
|
+
/**
|
73
|
+
* Get the closest matching element up the DOM tree
|
74
|
+
* @param {Element} elem Starting element
|
75
|
+
* @param {String} selector Selector to match against (class, ID, or data attribute)
|
76
|
+
* @return {Boolean|Element} Returns false if not match found
|
77
|
+
*/
|
78
|
+
var getClosest = function (elem, selector) {
|
79
|
+
var firstChar = selector.charAt(0);
|
80
|
+
for (; elem && elem !== document; elem = elem.parentNode) {
|
81
|
+
if (firstChar === ".") {
|
82
|
+
if (elem.classList.contains(selector.substr(1))) {
|
83
|
+
return elem;
|
84
|
+
}
|
85
|
+
} else if (firstChar === "#") {
|
86
|
+
if (elem.id === selector.substr(1)) {
|
87
|
+
return elem;
|
88
|
+
}
|
89
|
+
} else if (firstChar === "[") {
|
90
|
+
if (elem.hasAttribute(selector.substr(1, selector.length - 2))) {
|
91
|
+
return elem;
|
92
|
+
}
|
93
|
+
}
|
94
|
+
}
|
95
|
+
return false;
|
96
|
+
};
|
97
|
+
|
98
|
+
|
99
|
+
/**
|
100
|
+
* Merge defaults with user options
|
101
|
+
* @private
|
102
|
+
* @param {Object} defaults Default settings
|
103
|
+
* @param {Object} options User options
|
104
|
+
* @returns {Object} Merged values of defaults and options
|
105
|
+
*/
|
106
|
+
var extend = function (defaults, options) {
|
107
|
+
var extended = {};
|
108
|
+
forEach(defaults, function (value, prop) {
|
109
|
+
extended[prop] = defaults[prop];
|
110
|
+
});
|
111
|
+
forEach(options, function (value, prop) {
|
112
|
+
extended[prop] = options[prop];
|
113
|
+
});
|
114
|
+
return extended;
|
115
|
+
};
|
116
|
+
|
117
|
+
|
118
|
+
/**
|
119
|
+
* Debounced resize to throttle execution
|
120
|
+
* @param func
|
121
|
+
* @param wait
|
122
|
+
* @param immediate
|
123
|
+
* @returns {Function}
|
124
|
+
*/
|
125
|
+
function debounce(func, wait, immediate) {
|
126
|
+
var timeout;
|
127
|
+
return function () {
|
128
|
+
var context = this, args = arguments;
|
129
|
+
var later = function () {
|
130
|
+
timeout = null;
|
131
|
+
if (!immediate) func.apply(context, args);
|
132
|
+
};
|
133
|
+
var callNow = immediate && !timeout;
|
134
|
+
clearTimeout(timeout);
|
135
|
+
timeout = setTimeout(later, wait);
|
136
|
+
if (callNow) func.apply(context, args);
|
137
|
+
};
|
138
|
+
}
|
139
|
+
|
140
|
+
|
141
|
+
/**
|
142
|
+
* Toggle class on element
|
143
|
+
* @param el
|
144
|
+
* @param className
|
145
|
+
*/
|
146
|
+
var toggleClass = function (el, className) {
|
147
|
+
if (el.classList) {
|
148
|
+
el.classList.toggle(className);
|
149
|
+
} else {
|
150
|
+
var classes = el.className.split(" ");
|
151
|
+
var existingIndex = classes.indexOf(className);
|
152
|
+
|
153
|
+
if (existingIndex >= 0)
|
154
|
+
classes.splice(existingIndex, 1); else
|
155
|
+
classes.push(className);
|
156
|
+
|
157
|
+
el.className = classes.join(" ");
|
158
|
+
}
|
159
|
+
};
|
160
|
+
|
161
|
+
|
162
|
+
/**
|
163
|
+
* Check if dropdown menu is already on page before creating it
|
164
|
+
* @param mainNavWrapper
|
165
|
+
*/
|
166
|
+
var prepareHtml = function (_this, settings) {
|
167
|
+
|
168
|
+
/**
|
169
|
+
* Create dropdow menu
|
170
|
+
* @type {HTMLElement}
|
171
|
+
*/
|
172
|
+
toggleWrapper = document.createElement("span");
|
173
|
+
navDropdown = document.createElement("ul");
|
174
|
+
navDropdownToggle = document.createElement("button");
|
175
|
+
|
176
|
+
/**
|
177
|
+
* Set label for dropdown toggle
|
178
|
+
* @type {string}
|
179
|
+
*/
|
180
|
+
navDropdownToggle.innerHTML = settings.navDropdownLabel;
|
181
|
+
|
182
|
+
|
183
|
+
/**
|
184
|
+
* Move elements to the right spot
|
185
|
+
*/
|
186
|
+
_this.insertAfter(toggleWrapper, _this.querySelector(mainNav));
|
187
|
+
|
188
|
+
toggleWrapper.appendChild(navDropdown);
|
189
|
+
toggleWrapper.appendChild(navDropdownToggle);
|
190
|
+
|
191
|
+
/**
|
192
|
+
* Add classes so we can target elements
|
193
|
+
*/
|
194
|
+
navDropdown.classList.add(settings.navDropdown);
|
195
|
+
navDropdown.classList.add("priority-nav__dropdown");
|
196
|
+
|
197
|
+
navDropdownToggle.classList.add(settings.navDropdownToggle);
|
198
|
+
navDropdownToggle.classList.add("priority-nav__dropdown-toggle");
|
199
|
+
|
200
|
+
toggleWrapper.classList.add(settings.navDropdown+"-wrapper");
|
201
|
+
toggleWrapper.classList.add("priority-nav__wrapper");
|
202
|
+
|
203
|
+
_this.classList.add("priority-nav");
|
204
|
+
};
|
205
|
+
|
206
|
+
|
207
|
+
/**
|
208
|
+
* Get innerwidth without padding
|
209
|
+
* @param element
|
210
|
+
* @returns {number}
|
211
|
+
*/
|
212
|
+
var getElementContentWidth = function(element) {
|
213
|
+
var styles = window.getComputedStyle(element);
|
214
|
+
var padding = parseFloat(styles.paddingLeft) +
|
215
|
+
parseFloat(styles.paddingRight);
|
216
|
+
|
217
|
+
return element.clientWidth - padding;
|
218
|
+
};
|
219
|
+
|
220
|
+
|
221
|
+
/**
|
222
|
+
* Get viewport size
|
223
|
+
* @returns {{width: number, height: number}}
|
224
|
+
*/
|
225
|
+
var viewportSize = function() {
|
226
|
+
var doc = document, w = window;
|
227
|
+
var docEl = (doc.compatMode && doc.compatMode === "CSS1Compat")?
|
228
|
+
doc.documentElement: doc.body;
|
229
|
+
|
230
|
+
var width = docEl.clientWidth;
|
231
|
+
var height = docEl.clientHeight;
|
232
|
+
|
233
|
+
// mobile zoomed in?
|
234
|
+
if ( w.innerWidth && width > w.innerWidth ) {
|
235
|
+
width = w.innerWidth;
|
236
|
+
height = w.innerHeight;
|
237
|
+
}
|
238
|
+
|
239
|
+
return {width: width, height: height};
|
240
|
+
};
|
241
|
+
|
242
|
+
|
243
|
+
/**
|
244
|
+
* Get width
|
245
|
+
* @param elem
|
246
|
+
* @returns {number}
|
247
|
+
*/
|
248
|
+
var calculateWidths = function (_this) {
|
249
|
+
totalWidth = getElementContentWidth(_this);
|
250
|
+
//Check if parent is the navwrapper before calculating its width
|
251
|
+
if (_this.querySelector(navDropdown).parentNode === _this) {
|
252
|
+
dropDownWidth = _this.querySelector(navDropdown).offsetWidth;
|
253
|
+
} else {
|
254
|
+
dropDownWidth = 0;
|
255
|
+
}
|
256
|
+
restWidth = getChildrenWidth(_this) + settings.offsetPixels;
|
257
|
+
viewportWidth = viewportSize().width;
|
258
|
+
};
|
259
|
+
|
260
|
+
|
261
|
+
/**
|
262
|
+
* Move item to array
|
263
|
+
* @param item
|
264
|
+
*/
|
265
|
+
priorityNav.doesItFit = function (_this, settings) {
|
266
|
+
|
267
|
+
/**
|
268
|
+
* Check if it is the first run
|
269
|
+
*/
|
270
|
+
var delay = _this.getAttribute("instance") === 0 ? delay : settings.throttleDelay;
|
271
|
+
|
272
|
+
/**
|
273
|
+
* Increase instance
|
274
|
+
*/
|
275
|
+
instance++;
|
276
|
+
|
277
|
+
/**
|
278
|
+
* Debounced execution of the main logic
|
279
|
+
*/
|
280
|
+
(debounce(function () {
|
281
|
+
|
282
|
+
/**
|
283
|
+
* Get the current element"s instance
|
284
|
+
* @type {string}
|
285
|
+
*/
|
286
|
+
var identifier = _this.getAttribute("instance");
|
287
|
+
|
288
|
+
/**
|
289
|
+
* Update width
|
290
|
+
*/
|
291
|
+
calculateWidths(_this);
|
292
|
+
|
293
|
+
/**
|
294
|
+
* Keep executing until all menu items that are overflowing are moved
|
295
|
+
*/
|
296
|
+
while (totalWidth <= restWidth && _this.querySelector(mainNav).children.length > 0 || viewportWidth < settings.breakPoint && _this.querySelector(mainNav).children.length > 0) {
|
297
|
+
//move item to dropdown
|
298
|
+
priorityNav.toDropdown(_this, identifier);
|
299
|
+
//recalculate widths
|
300
|
+
calculateWidths(_this, identifier);
|
301
|
+
//update dropdownToggle label
|
302
|
+
if(viewportWidth < settings.breakPoint) updateLabel(_this, identifier, settings.navDropdownBreakpointLabel);
|
303
|
+
}
|
304
|
+
|
305
|
+
/**
|
306
|
+
* Keep executing until all menu items that are able to move back are moved
|
307
|
+
*/
|
308
|
+
while (totalWidth >= breaks[identifier][breaks[identifier].length - 1] && viewportWidth > settings.breakPoint) {
|
309
|
+
//move item to menu
|
310
|
+
priorityNav.toMenu(_this, identifier);
|
311
|
+
//update dropdownToggle label
|
312
|
+
if(viewportWidth > settings.breakPoint) updateLabel(_this, identifier, settings.navDropdownLabel);
|
313
|
+
}
|
314
|
+
|
315
|
+
/**
|
316
|
+
* If there are no items in dropdown hide dropdown
|
317
|
+
*/
|
318
|
+
if (breaks[identifier].length < 1) {
|
319
|
+
_this.querySelector(navDropdown).classList.remove("show");
|
320
|
+
//show navDropdownLabel
|
321
|
+
updateLabel(_this, identifier, settings.navDropdownLabel);
|
322
|
+
}
|
323
|
+
|
324
|
+
/**
|
325
|
+
* If there are no items in menu
|
326
|
+
*/
|
327
|
+
if (_this.querySelector(mainNav).children.length < 1){
|
328
|
+
//show navDropdownBreakpointLabel
|
329
|
+
updateLabel(_this, identifier, settings.navDropdownBreakpointLabel);
|
330
|
+
}
|
331
|
+
|
332
|
+
/**
|
333
|
+
* Check if we need to show toggle menu button
|
334
|
+
*/
|
335
|
+
showToggle(_this, identifier);
|
336
|
+
|
337
|
+
}, delay ))();
|
338
|
+
};
|
339
|
+
|
340
|
+
|
341
|
+
/**
|
342
|
+
* Show/hide toggle button
|
343
|
+
*/
|
344
|
+
var showToggle = function (_this, identifier) {
|
345
|
+
if (breaks[identifier].length < 1) {
|
346
|
+
_this.querySelector(navDropdownToggle).classList.add("priority-nav-is-hidden");
|
347
|
+
_this.querySelector(navDropdownToggle).classList.remove("priority-nav-is-visible");
|
348
|
+
_this.classList.remove("priority-nav-has-dropdown");
|
349
|
+
} else {
|
350
|
+
_this.querySelector(navDropdownToggle).classList.add("priority-nav-is-visible");
|
351
|
+
_this.querySelector(navDropdownToggle).classList.remove("priority-nav-is-hidden");
|
352
|
+
_this.classList.add("priority-nav-has-dropdown");
|
353
|
+
}
|
354
|
+
};
|
355
|
+
|
356
|
+
|
357
|
+
/**
|
358
|
+
* Update count on dropdown toggle button
|
359
|
+
*/
|
360
|
+
var updateCount = function (_this, identifier) {
|
361
|
+
_this.querySelector(navDropdownToggle).setAttribute("priorityNav-count", breaks[identifier].length);
|
362
|
+
};
|
363
|
+
|
364
|
+
var updateLabel = function(_this, identifier, label){
|
365
|
+
_this.querySelector(navDropdownToggle).innerHTML = label;
|
366
|
+
};
|
367
|
+
|
368
|
+
|
369
|
+
/**
|
370
|
+
* Move item to dropdown
|
371
|
+
*/
|
372
|
+
priorityNav.toDropdown = function (_this, identifier) {
|
373
|
+
|
374
|
+
|
375
|
+
/**
|
376
|
+
* move last child of navigation menu to dropdown
|
377
|
+
*/
|
378
|
+
if (_this.querySelector(navDropdown).firstChild && _this.querySelector(mainNav).children.length > 0) {
|
379
|
+
_this.querySelector(navDropdown).insertBefore(_this.querySelector(mainNav).lastElementChild, _this.querySelector(navDropdown).firstChild);
|
380
|
+
} else if (_this.querySelector(mainNav).children.length > 0) {
|
381
|
+
_this.querySelector(navDropdown).appendChild(_this.querySelector(mainNav).lastElementChild);
|
382
|
+
}
|
383
|
+
|
384
|
+
/**
|
385
|
+
* store breakpoints
|
386
|
+
*/
|
387
|
+
breaks[identifier].push(restWidth);
|
388
|
+
|
389
|
+
/**
|
390
|
+
* check if we need to show toggle menu button
|
391
|
+
*/
|
392
|
+
showToggle(_this, identifier);
|
393
|
+
|
394
|
+
/**
|
395
|
+
* update count on dropdown toggle button
|
396
|
+
*/
|
397
|
+
if (_this.querySelector(mainNav).children.length > 0 && settings.count) {
|
398
|
+
updateCount(_this, identifier);
|
399
|
+
}
|
400
|
+
|
401
|
+
/**
|
402
|
+
* If item has been moved to dropdown trigger the callback
|
403
|
+
*/
|
404
|
+
settings.moved();
|
405
|
+
};
|
406
|
+
|
407
|
+
|
408
|
+
/**
|
409
|
+
* Move item to menu
|
410
|
+
*/
|
411
|
+
priorityNav.toMenu = function (_this, identifier) {
|
412
|
+
|
413
|
+
/**
|
414
|
+
* move last child of navigation menu to dropdown
|
415
|
+
*/
|
416
|
+
if (_this.querySelector(navDropdown).children.length > 0) _this.querySelector(mainNav).appendChild(_this.querySelector(navDropdown).firstElementChild);
|
417
|
+
|
418
|
+
/**
|
419
|
+
* remove last breakpoint
|
420
|
+
*/
|
421
|
+
breaks[identifier].pop();
|
422
|
+
|
423
|
+
/**
|
424
|
+
* Check if we need to show toggle menu button
|
425
|
+
*/
|
426
|
+
showToggle(_this, identifier);
|
427
|
+
|
428
|
+
/**
|
429
|
+
* update count on dropdown toggle button
|
430
|
+
*/
|
431
|
+
if (_this.querySelector(mainNav).children.length > 0 && settings.count) {
|
432
|
+
updateCount(_this, identifier);
|
433
|
+
}
|
434
|
+
|
435
|
+
/**
|
436
|
+
* If item has been moved back to the main menu trigger the callback
|
437
|
+
*/
|
438
|
+
settings.movedBack();
|
439
|
+
};
|
440
|
+
|
441
|
+
|
442
|
+
/**
|
443
|
+
* Count width of children and return the value
|
444
|
+
* @param e
|
445
|
+
*/
|
446
|
+
var getChildrenWidth = function (e) {
|
447
|
+
var children = e.childNodes;
|
448
|
+
var sum = 0;
|
449
|
+
for (var i = 0; i < children.length; i++) {
|
450
|
+
if (children[i].nodeType !== 3) {
|
451
|
+
if(!isNaN(children[i].offsetWidth)){
|
452
|
+
sum += children[i].offsetWidth;
|
453
|
+
}
|
454
|
+
|
455
|
+
}
|
456
|
+
}
|
457
|
+
return sum;
|
458
|
+
};
|
459
|
+
|
460
|
+
|
461
|
+
/**
|
462
|
+
* Bind eventlisteners
|
463
|
+
*/
|
464
|
+
var listeners = function (_this, settings) {
|
465
|
+
|
466
|
+
// Check if an item needs to move
|
467
|
+
if(window.attachEvent) {
|
468
|
+
window.attachEvent("onresize", function() {
|
469
|
+
if(priorityNav.doesItFit)priorityNav.doesItFit(_this, settings);
|
470
|
+
});
|
471
|
+
}
|
472
|
+
else if(window.addEventListener) {
|
473
|
+
window.addEventListener("resize", function() {
|
474
|
+
if(priorityNav.doesItFit)priorityNav.doesItFit(_this, settings);
|
475
|
+
}, true);
|
476
|
+
}
|
477
|
+
|
478
|
+
// Toggle dropdown
|
479
|
+
_this.querySelector(navDropdownToggle).addEventListener("click", function () {
|
480
|
+
toggleClass(_this.querySelector(navDropdown), "show");
|
481
|
+
toggleClass(this, "is-open");
|
482
|
+
toggleClass(_this, "is-open");
|
483
|
+
});
|
484
|
+
|
485
|
+
/*
|
486
|
+
* Remove when clicked outside dropdown
|
487
|
+
*/
|
488
|
+
document.addEventListener("click", function (event) {
|
489
|
+
if (!getClosest(event.target, settings.navDropdown) && event.target !== _this.querySelector(navDropdownToggle)) {
|
490
|
+
_this.querySelector(navDropdown).classList.remove("show");
|
491
|
+
_this.querySelector(navDropdownToggle).classList.remove("is-open");
|
492
|
+
_this.classList.remove("is-open");
|
493
|
+
}
|
494
|
+
});
|
495
|
+
|
496
|
+
/**
|
497
|
+
* Remove when escape key is pressed
|
498
|
+
*/
|
499
|
+
document.onkeydown = function (evt) {
|
500
|
+
evt = evt || window.event;
|
501
|
+
if (evt.keyCode === 27) {
|
502
|
+
navDropdown.classList.remove("show");
|
503
|
+
navDropdownToggle.classList.remove("is-open");
|
504
|
+
mainNavWrapper.classList.remove("is-open");
|
505
|
+
}
|
506
|
+
};
|
507
|
+
};
|
508
|
+
|
509
|
+
|
510
|
+
/**
|
511
|
+
* Remove function
|
512
|
+
*/
|
513
|
+
Element.prototype.remove = function() {
|
514
|
+
this.parentElement.removeChild(this);
|
515
|
+
};
|
516
|
+
|
517
|
+
/*global HTMLCollection */
|
518
|
+
NodeList.prototype.remove = HTMLCollection.prototype.remove = function() {
|
519
|
+
for(var i = 0, len = this.length; i < len; i++) {
|
520
|
+
if(this[i] && this[i].parentElement) {
|
521
|
+
this[i].parentElement.removeChild(this[i]);
|
522
|
+
}
|
523
|
+
}
|
524
|
+
};
|
525
|
+
|
526
|
+
|
527
|
+
/**
|
528
|
+
* Destroy the current initialization.
|
529
|
+
* @public
|
530
|
+
*/
|
531
|
+
priorityNav.destroy = function () {
|
532
|
+
// If plugin isn"t already initialized, stop
|
533
|
+
if (!settings) return;
|
534
|
+
// Remove feedback class
|
535
|
+
document.documentElement.classList.remove(settings.initClass);
|
536
|
+
// Remove toggle
|
537
|
+
toggleWrapper.remove();
|
538
|
+
// Remove settings
|
539
|
+
settings = null;
|
540
|
+
delete priorityNav.init;
|
541
|
+
delete priorityNav.doesItFit;
|
542
|
+
};
|
543
|
+
|
544
|
+
|
545
|
+
/**
|
546
|
+
* insertAfter function
|
547
|
+
* @param n
|
548
|
+
* @param r
|
549
|
+
*/
|
550
|
+
if (supports && typeof Node !== 'undefined'){
|
551
|
+
Node.prototype.insertAfter = function(n,r) {this.insertBefore(n,r.nextSibling);};
|
552
|
+
}
|
553
|
+
|
554
|
+
|
555
|
+
/**
|
556
|
+
* Initialize Plugin
|
557
|
+
* @public
|
558
|
+
* @param {Object} options User settings
|
559
|
+
*/
|
560
|
+
priorityNav.init = function (options) {
|
561
|
+
|
562
|
+
|
563
|
+
// Feature test.
|
564
|
+
if (!supports && typeof Node === 'undefined'){
|
565
|
+
console.warn("This browser doesn't support priorityNav");
|
566
|
+
return;
|
567
|
+
}
|
568
|
+
|
569
|
+
/**
|
570
|
+
* Merge user options with defaults
|
571
|
+
* @type {Object}
|
572
|
+
*/
|
573
|
+
settings = extend(defaults, options || {});
|
574
|
+
|
575
|
+
/**
|
576
|
+
* Store nodes
|
577
|
+
* @type {NodeList}
|
578
|
+
*/
|
579
|
+
var elements = document.querySelectorAll(settings.mainNavWrapper);
|
580
|
+
|
581
|
+
/**
|
582
|
+
* Loop over every instance and reference _this
|
583
|
+
*/
|
584
|
+
forEach(elements, function(_this){
|
585
|
+
|
586
|
+
/**
|
587
|
+
* Create breaks array
|
588
|
+
* @type {number}
|
589
|
+
*/
|
590
|
+
breaks[count] = [];
|
591
|
+
|
592
|
+
/**
|
593
|
+
* Set the instance number as data attribute
|
594
|
+
*/
|
595
|
+
_this.setAttribute("instance", count++);
|
596
|
+
|
597
|
+
/**
|
598
|
+
* Store the wrapper element
|
599
|
+
*/
|
600
|
+
mainNavWrapper = _this;
|
601
|
+
if (!mainNavWrapper) {
|
602
|
+
console.warn("couldn't find the specified mainNavWrapper element");
|
603
|
+
return;
|
604
|
+
}
|
605
|
+
|
606
|
+
/**
|
607
|
+
* Store the menu elementStore the menu element
|
608
|
+
*/
|
609
|
+
mainNav = settings.mainNav;
|
610
|
+
if (!_this.querySelector(mainNav)) {
|
611
|
+
console.warn("couldn't find the specified mainNav element");
|
612
|
+
return;
|
613
|
+
}
|
614
|
+
|
615
|
+
/**
|
616
|
+
* Check if we need to create the dropdown elements
|
617
|
+
*/
|
618
|
+
prepareHtml(_this, settings);
|
619
|
+
|
620
|
+
/**
|
621
|
+
* Store the dropdown element
|
622
|
+
*/
|
623
|
+
navDropdown = "."+settings.navDropdown;
|
624
|
+
if (!_this.querySelector(navDropdown)) {
|
625
|
+
console.warn("couldn't find the specified navDropdown element");
|
626
|
+
return;
|
627
|
+
}
|
628
|
+
|
629
|
+
/**
|
630
|
+
* Store the dropdown toggle element
|
631
|
+
*/
|
632
|
+
navDropdownToggle = "."+settings.navDropdownToggle;
|
633
|
+
if (!_this.querySelector(navDropdownToggle)) {
|
634
|
+
console.warn("couldn't find the specified navDropdownToggle element");
|
635
|
+
return;
|
636
|
+
}
|
637
|
+
|
638
|
+
/**
|
639
|
+
* Event listeners
|
640
|
+
*/
|
641
|
+
listeners(_this, settings);
|
642
|
+
|
643
|
+
/**
|
644
|
+
* Start first check
|
645
|
+
*/
|
646
|
+
priorityNav.doesItFit(_this, settings);
|
647
|
+
|
648
|
+
});
|
649
|
+
|
650
|
+
/**
|
651
|
+
* Count amount of instances
|
652
|
+
*/
|
653
|
+
instance++;
|
654
|
+
|
655
|
+
/**
|
656
|
+
* Add class to HTML element to activate conditional CSS
|
657
|
+
*/
|
658
|
+
document.documentElement.classList.add(settings.initClass);
|
659
|
+
};
|
660
|
+
|
661
|
+
|
662
|
+
/**
|
663
|
+
* Public APIs
|
664
|
+
*/
|
665
|
+
return priorityNav;
|
666
|
+
|
667
667
|
});
|