From a4ee5de9fc6586952c6e77a3fbd914f280828a65 Mon Sep 17 00:00:00 2001 From: dpilafian Date: Sun, 18 Jun 2023 01:20:41 -0700 Subject: [PATCH] Switch to native events and elements --- README.md | 12 +---- docs/app.js | 42 +++++++++++---- docs/hamburger-menu.css | 2 +- docs/hamburger-menu.js | 82 +++++++++++++++++++++--------- docs/index.html | 18 +++---- docs/multipage/about.html | 5 +- docs/multipage/index.html | 5 +- docs/multipage/products/index.html | 5 +- docs/multipage/products/x3000.html | 5 +- docs/multipage/products/x3200.html | 5 +- docs/multipage/support.html | 5 +- docs/single-page-app/index.html | 5 +- package.json | 15 +++--- src/hamburger-menu.css | 2 +- src/hamburger-menu.js | 82 +++++++++++++++++++++--------- 15 files changed, 180 insertions(+), 110 deletions(-) diff --git a/README.md b/README.md index 7a51f9d..2c230ae 100644 --- a/README.md +++ b/README.md @@ -28,7 +28,6 @@ Include the **HamburgerMenu** CSS and JavaScript: ... ... - ... ``` @@ -37,7 +36,6 @@ Include the **HamburgerMenu** CSS and JavaScript: ... ... - ... ``` @@ -110,19 +108,13 @@ Example of highlighting the menu item for "**Page 2**":
  • Page 2
  • ``` -...and an equivalent example using jQuery: -```javascript -$('nav.hamburger-menu').find('a[href=page2.html]').parent().addClass('current'); -``` - **Note:**
    To support old legacy web browsers, add a polyfill for [URL](https://www.npmjs.com/package/url-polyfill) to your website. -## F) Removing jQuery Dependency -The `hamburger-menu.js` file depends on jQuery, but you can eliminate **both** jQuery -and the `hamburger-menu.js` file by incorporating this one line of JavaScript in your website: +## F) hamburger-menu.js File is Optional +You can the `hamburger-menu.js` file by incorporating this one line of JavaScript in your website: ```javascript document.addEventListener('click', () => {}); //workaround for sticky hover on mobile ``` diff --git a/docs/app.js b/docs/app.js index a35a144..105bc5d 100644 --- a/docs/app.js +++ b/docs/app.js @@ -5,21 +5,41 @@ License: MIT */ const app = { + pulse(elem, text) { + elem.style.opacity = '0'; + elem.style.transition = 'all 0ms'; + if (text !== undefined) + elem.textContent = text; + const animate = () => { + elem.style.opacity = '1'; + elem.style.transition = 'all 500ms'; + }; + globalThis.requestAnimationFrame(animate); + }, actionClick(event) { - const title = $(event.target).closest('li').find('span').first().text(); - $('main >h1').hide().text(title).fadeIn(); + const menuItemSelector = 'body.single-page-app nav.hamburger-menu aside ul li span'; + const elem = event.target.closest(menuItemSelector); + const displayTitle = () => { + const title = elem.closest('li').querySelector('span').textContent; + const header = globalThis.document.querySelector('main >h1'); + app.pulse(header, title); + }; + if (elem) + displayTitle(); }, - setupIcons() { - const getName = (elem) => elem.data().icon || elem.data().brand; - const makeIcon = (i, elem) => $(elem).addClass('font-icon fa-' + getName($(elem))); - $('i[data-icon]').addClass('fas').each(makeIcon); - $('i[data-brand]').addClass('fab').each(makeIcon); + makeIcons(type, selector, addClass) { + const iconify = (elem) => { + elem.classList.add('font-icon'); + elem.classList.add(addClass); + elem.classList.add('fa-' + elem.dataset[type]); + }; + globalThis.document.querySelectorAll(selector).forEach(iconify); }, setup() { - app.setupIcons(); - const menuItemSelector = 'body.single-page-app nav.hamburger-menu aside ul li span'; - $(globalThis.document).on({ click: app.actionClick }, menuItemSelector); + app.makeIcons('icon', 'i[data-icon]', 'fas'); + app.makeIcons('brand', 'i[data-brand]', 'fab'); + globalThis.document.addEventListener('click', app.actionClick); }, }; -app.setup(); +hamburgerMenu.dom.onReady(app.setup); diff --git a/docs/hamburger-menu.css b/docs/hamburger-menu.css index 56fb288..73ac9fe 100644 --- a/docs/hamburger-menu.css +++ b/docs/hamburger-menu.css @@ -63,7 +63,7 @@ nav.hamburger-menu aside ul ul li span { font-size: 0.8rem; padding: 4px 0px 4px 30px; } -@media (max-width: 667px) { /* selects iPhone 6/6s/7/8/SE(2020) landscape and anything narrower */ +@media (max-width: 667px) { /* selects iPhone 6/6s/7/8/SE2/SE3 landscape and anything narrower */ html body { padding: 35px 10px 0px 10px; cursor: pointer; /* enables closing menu */ diff --git a/docs/hamburger-menu.js b/docs/hamburger-menu.js index 54350cd..2886f1a 100644 --- a/docs/hamburger-menu.js +++ b/docs/hamburger-menu.js @@ -1,50 +1,82 @@ //! hamburger-menu v0.5.2 ☰ https://github.com/center-key/hamburger-menu ☰ MIT License const hamburgerMenu = { + // version: '0.5.2', - selectItem(event) { - const item = $(event.target).closest('li'); - item.closest('aside').find('li').removeClass('current'); - item.addClass('current'); - const nav = item.closest('.hamburger-menu').addClass('collapse-menu'); - const eventRoutes = {}; + selectItem(elem) { + const menuItem = elem.closest('li'); + const navMenu = menuItem.closest('.hamburger-menu'); + const reset = (elem) => elem.classList.remove('current'); + menuItem.closest('aside').querySelectorAll('li').forEach(reset); + menuItem.classList.add('current'); + navMenu.classList.add('collapse-menu'); + navMenu.dataset.menuCollapsed = String(Date.now()); const restoreAllowExand = () => { - nav.removeClass('collapse-menu'); - console.log('restoreAllowExand'); - $(globalThis.document).off(eventRoutes); + navMenu.classList.remove('collapse-menu'); + globalThis.document.removeEventListener('click', restoreAllowExand); + globalThis.document.removeEventListener('mousemove', restoreAllowExand); + }; + const listen = () => { + globalThis.document.addEventListener('click', restoreAllowExand); + globalThis.document.addEventListener('mousemove', restoreAllowExand); }; - eventRoutes.click = restoreAllowExand; - eventRoutes.mousemove = restoreAllowExand; const afterCurrentClick = 100; - globalThis.setTimeout(() => $(globalThis.document).on(eventRoutes), afterCurrentClick); + globalThis.setTimeout(listen, afterCurrentClick); }, setup() { - $(globalThis.document).on({ click: $.noop }); //workaround for sticky hover on mobile - const nav = $('nav.hamburger-menu'); + globalThis.document.addEventListener('click', () => {}); //workaround for sticky hover on mobile + const navMenu = globalThis.document.querySelector('.hamburger-menu'); + const aside = navMenu?.querySelector('aside'); const autoHighlightMultiPage = () => { - const current = { - url: new globalThis.URL(globalThis.location.href), - path: globalThis.location.pathname.replace(/\/$/, ''), - }; - const isCurrent = (i, elem) => { - const linkUrl = new globalThis.URL($(elem).attr('href'), current.url); - return linkUrl.pathname.replace(/\/$/, '') === current.path; + const currentUrl = new globalThis.URL(globalThis.location.href); + const currentPath = globalThis.location.pathname.replace(/\/$/, ''); + const setCurrent = (elem) => { + const linkUrl = new globalThis.URL(elem.href, currentUrl); + const isCurrent = linkUrl.pathname.replace(/\/$/, '') === currentPath; + elem.parentElement.classList.add(isCurrent ? 'current' : 'other-page'); }; - nav.find('li >a').filter(isCurrent).first().closest('li').addClass('current'); + navMenu.querySelectorAll('li >a').forEach(setCurrent); + }; + const delegateSelectItem = (event) => { + const elem = event.target.closest('.hamburger-menu li'); + if (elem) + hamburgerMenu.selectItem(elem); }; const autoHighlightSinglePageApp = () => - nav.find('>aside li').on({ click: hamburgerMenu.selectItem }); + globalThis.document.addEventListener('click', delegateSelectItem); const autoHighlight = () => { autoHighlightMultiPage(); autoHighlightSinglePageApp(); }; - if (!nav.find('>aside').hasClass('disable-auto-highlight')) + if (aside && !aside.classList.contains('disable-auto-highlight')) autoHighlight(); }, + dom: { + onReady(callback) { + // Calls the specified function once the web page is loaded and ready. + // Example (execute myApp.setup() as soon as the DOM is interactive): + // hamburgerMenu.dom.onReady(myApp.setup); + if (globalThis.document.readyState === 'complete') + callback(); + else + globalThis.window.addEventListener('DOMContentLoaded', callback); + }, + }, + }; -$(hamburgerMenu.setup); +hamburgerMenu.dom.onReady(hamburgerMenu.setup); diff --git a/docs/index.html b/docs/index.html index 0f817da..7cbc118 100644 --- a/docs/index.html +++ b/docs/index.html @@ -9,9 +9,9 @@ - - - + + + - - - + + - + diff --git a/docs/multipage/about.html b/docs/multipage/about.html index 702d3e8..55ba5c0 100644 --- a/docs/multipage/about.html +++ b/docs/multipage/about.html @@ -12,13 +12,12 @@ HamburgerMenu ☰ Multipage - + - - + diff --git a/docs/multipage/index.html b/docs/multipage/index.html index cdddd30..fce80e2 100644 --- a/docs/multipage/index.html +++ b/docs/multipage/index.html @@ -12,13 +12,12 @@ HamburgerMenu ☰ Multipage - + - - + diff --git a/docs/multipage/products/index.html b/docs/multipage/products/index.html index 16ada83..dcb19a4 100644 --- a/docs/multipage/products/index.html +++ b/docs/multipage/products/index.html @@ -12,13 +12,12 @@ HamburgerMenu ☰ Multipage - + - - + diff --git a/docs/multipage/products/x3000.html b/docs/multipage/products/x3000.html index 6613934..63653cc 100644 --- a/docs/multipage/products/x3000.html +++ b/docs/multipage/products/x3000.html @@ -12,13 +12,12 @@ HamburgerMenu ☰ Multipage - + - - + diff --git a/docs/multipage/products/x3200.html b/docs/multipage/products/x3200.html index ba11880..ede6a41 100644 --- a/docs/multipage/products/x3200.html +++ b/docs/multipage/products/x3200.html @@ -12,13 +12,12 @@ HamburgerMenu ☰ Multipage - + - - + diff --git a/docs/multipage/support.html b/docs/multipage/support.html index 2b6d357..2b9fe0d 100644 --- a/docs/multipage/support.html +++ b/docs/multipage/support.html @@ -12,13 +12,12 @@ HamburgerMenu ☰ Multipage - + - - + diff --git a/docs/single-page-app/index.html b/docs/single-page-app/index.html index e949086..15407fd 100644 --- a/docs/single-page-app/index.html +++ b/docs/single-page-app/index.html @@ -12,13 +12,12 @@ HamburgerMenu ☰ Single-Page Web App - + - - + diff --git a/package.json b/package.json index af1c583..b52d5dc 100644 --- a/package.json +++ b/package.json @@ -33,13 +33,15 @@ "undef": true, "unused": true, "browser": true, - "jquery": true, "node": true, - "mocha": true + "mocha": true, + "globals": { + "hamburgerMenu": true + } }, "runScriptsConfig": { "clean": [ - "rimraf build dist" + "rimraf build dist docs/hamburger-menu.js" ], "build": [ "jshint . --exclude-path .gitignore", @@ -56,10 +58,9 @@ "scripts": { "pretest": "run-scripts clean build", "test": "mocha spec/*.spec.js", - "interactive": "browser-sync docs --watch" + "interactive": "browser-sync . --startPath docs --files docs" }, "dependencies": { - "jquery": "~3.6" }, "devDependencies": { "@fortawesome/fontawesome-free": "~6.4", @@ -69,7 +70,7 @@ "copy-file-util": "~1.0", "copy-folder-util": "~1.0", "csso-cli": "~4.0", - "dna-engine": "~2.3", + "dna-engine": "~3.0", "jshint": "~2.13", "mocha": "~10.2", "replacer-util": "~1.0", @@ -77,6 +78,6 @@ "run-scripts-util": "~1.1", "uglify-js": "~3.17", "w3c-html-validator": "~1.3", - "web-ignition": "~1.7" + "web-ignition": "~2.0" } } diff --git a/src/hamburger-menu.css b/src/hamburger-menu.css index 89bfebb..07d788b 100644 --- a/src/hamburger-menu.css +++ b/src/hamburger-menu.css @@ -62,7 +62,7 @@ nav.hamburger-menu aside ul ul li span { font-size: 0.8rem; padding: 4px 0px 4px 30px; } -@media (max-width: 667px) { /* selects iPhone 6/6s/7/8/SE(2020) landscape and anything narrower */ +@media (max-width: 667px) { /* selects iPhone 6/6s/7/8/SE2/SE3 landscape and anything narrower */ html body { padding: 35px 10px 0px 10px; cursor: pointer; /* enables closing menu */ diff --git a/src/hamburger-menu.js b/src/hamburger-menu.js index 8918b57..16b58cf 100644 --- a/src/hamburger-menu.js +++ b/src/hamburger-menu.js @@ -1,50 +1,82 @@ // HamburgerMenu ☰ MIT License const hamburgerMenu = { + // version: '{{pkg.version}}', - selectItem(event) { - const item = $(event.target).closest('li'); - item.closest('aside').find('li').removeClass('current'); - item.addClass('current'); - const nav = item.closest('.hamburger-menu').addClass('collapse-menu'); - const eventRoutes = {}; + selectItem(elem) { + const menuItem = elem.closest('li'); + const navMenu = menuItem.closest('.hamburger-menu'); + const reset = (elem) => elem.classList.remove('current'); + menuItem.closest('aside').querySelectorAll('li').forEach(reset); + menuItem.classList.add('current'); + navMenu.classList.add('collapse-menu'); + navMenu.dataset.menuCollapsed = String(Date.now()); const restoreAllowExand = () => { - nav.removeClass('collapse-menu'); - console.log('restoreAllowExand'); - $(globalThis.document).off(eventRoutes); + navMenu.classList.remove('collapse-menu'); + globalThis.document.removeEventListener('click', restoreAllowExand); + globalThis.document.removeEventListener('mousemove', restoreAllowExand); + }; + const listen = () => { + globalThis.document.addEventListener('click', restoreAllowExand); + globalThis.document.addEventListener('mousemove', restoreAllowExand); }; - eventRoutes.click = restoreAllowExand; - eventRoutes.mousemove = restoreAllowExand; const afterCurrentClick = 100; - globalThis.setTimeout(() => $(globalThis.document).on(eventRoutes), afterCurrentClick); + globalThis.setTimeout(listen, afterCurrentClick); }, setup() { - $(globalThis.document).on({ click: $.noop }); //workaround for sticky hover on mobile - const nav = $('nav.hamburger-menu'); + globalThis.document.addEventListener('click', () => {}); //workaround for sticky hover on mobile + const navMenu = globalThis.document.querySelector('.hamburger-menu'); + const aside = navMenu?.querySelector('aside'); const autoHighlightMultiPage = () => { - const current = { - url: new globalThis.URL(globalThis.location.href), - path: globalThis.location.pathname.replace(/\/$/, ''), - }; - const isCurrent = (i, elem) => { - const linkUrl = new globalThis.URL($(elem).attr('href'), current.url); - return linkUrl.pathname.replace(/\/$/, '') === current.path; + const currentUrl = new globalThis.URL(globalThis.location.href); + const currentPath = globalThis.location.pathname.replace(/\/$/, ''); + const setCurrent = (elem) => { + const linkUrl = new globalThis.URL(elem.href, currentUrl); + const isCurrent = linkUrl.pathname.replace(/\/$/, '') === currentPath; + elem.parentElement.classList.add(isCurrent ? 'current' : 'other-page'); }; - nav.find('li >a').filter(isCurrent).first().closest('li').addClass('current'); + navMenu.querySelectorAll('li >a').forEach(setCurrent); + }; + const delegateSelectItem = (event) => { + const elem = event.target.closest('.hamburger-menu li'); + if (elem) + hamburgerMenu.selectItem(elem); }; const autoHighlightSinglePageApp = () => - nav.find('>aside li').on({ click: hamburgerMenu.selectItem }); + globalThis.document.addEventListener('click', delegateSelectItem); const autoHighlight = () => { autoHighlightMultiPage(); autoHighlightSinglePageApp(); }; - if (!nav.find('>aside').hasClass('disable-auto-highlight')) + if (aside && !aside.classList.contains('disable-auto-highlight')) autoHighlight(); }, + dom: { + onReady(callback) { + // Calls the specified function once the web page is loaded and ready. + // Example (execute myApp.setup() as soon as the DOM is interactive): + // hamburgerMenu.dom.onReady(myApp.setup); + if (globalThis.document.readyState === 'complete') + callback(); + else + globalThis.window.addEventListener('DOMContentLoaded', callback); + }, + }, + }; -$(hamburgerMenu.setup); +hamburgerMenu.dom.onReady(hamburgerMenu.setup);