jscript for toggling a hamburger menu not working - javascript

I am trying to fix some stuff left over by the front-end developer and I am not expert in javascript (not at all).
On our staging system we use a hamburger menu and for reasons unknown to me while the hamburger menu toggles perfectly on opening, upon first closing it doesn't revert back from an "X" to the "Hamburger" subsequent changes instead work flawlessly just that the state is inverted ("X" where there should be a hamburger and hamburger where there should be an "X").
So I did it half right (but only half :D).
My rather not beautyful code:
<script type="text/javascript">
var headerMobile = document.getElementsByClassName('header-mobile')[0];
var openMobileMenuTrigger = document.getElementById('open-mobile-menu');
var closeMobileMenuTrigger = document.getElementById('close-mobile-menu');
var mobileMainMenu = document.getElementById('header-mobile_menu');
var submenus = document.getElementsByClassName('header-mobile_submenu');
var openMobileSubMenuTriggers = document.getElementsByClassName('open-mobile-submenu');
var closeMobileSubMenuTriggers = document.getElementsByClassName('close-mobile-submenu');
var hamburgerHeader = document.getElementById('open-mobile-menu');
var openSearchTrigger = document.getElementById('open-search-field')
function toggleHamburger(){
if (hamburgerHeader.classList.contains('is-active')){
hamburgerHeader.classList.remove('is-active');
} else {
hamburgerHeader.classList.add('is-active');
}
}
function toggleMobileMenu(){
if(mobileMainMenu.classList.contains('is-active') !== true){
if (openSearchTrigger)
{openSearchTrigger.classList.remove('is-active')}
mobileMainMenu.classList.add('is-active');
headerMobile.classList.add('is-active'); //remove shadow
document.getElementsByTagName('html')[0].classList.add('noscroll')// no scroll for html
document.getElementsByTagName('body')[0].classList.add('noscroll')// no scroll for body
} else {
setTimeout(
function(){
// headerMobile.classList.remove('is-active'); // NO MORE SHADOW IF WE KEEP ACTIVE SEARCH MENU
for (var i = submenus.length - 1; i >= 0; i--) {
submenus[i].classList.remove('is-active');
}
},
400
)
if(openSearchTrigger){
openSearchTrigger.classList.add('is-active')
}
hamburgerHeader.classList.remove('is-active')
mobileMainMenu.classList.remove('is-active')
document.getElementsByTagName('html')[0].classList.remove('noscroll')// no scroll for html
document.getElementsByTagName('body')[0].classList.remove('noscroll')// no scroll for body
}
}
function openMobileSubmenu(event){
var submenuId = event.currentTarget.getAttribute('data-submenu');
document.getElementById('header-mobile_submenu-' + submenuId).classList.add('is-active');
}
function closeMobileSubmenu(){
for (var i = submenus.length - 1; i >= 0; i--) {
submenus[i].classList.remove('is-active');
}
}
if (openSearchTrigger){
openSearchTrigger.addEventListener('click', toggleMobileMenu);
}
openMobileMenuTrigger.addEventListener('click', toggleMobileMenu);
// closeMobileMenuTrigger.addEventListener('click', closeMobileMenu);
hamburgerHeader.addEventListener('click', toggleHamburger);
for (var i = openMobileSubMenuTriggers.length - 1; i >= 0; i--) {
openMobileSubMenuTriggers[i].addEventListener('click', openMobileSubmenu);
}
for (var i = closeMobileSubMenuTriggers.length - 1; i >= 0; i--) {
closeMobileSubMenuTriggers[i].addEventListener('click', closeMobileSubmenu);
}

Related

I have a problem calling with my carousel codes, it keeps keeping variables for other conditions

I have tried so many method to stop this error but nothing seems to work, here is a link to my codepen: https://codepen.io/T_manuel/pen/PoKMYVx
when the page loads, I call the function but when I resize the page, Javascript seems to do the new logic also with previous logics...
const mediaQuery_1 = '(max-width:601px)';
const mediaQueryList1 = window.matchMedia(mediaQuery_1);
/carousel parent div -- to dynamically load the carousel items
// define media queries
const mediaQuery_1 = '(max-width:601px)';
// add matchMedia
const mediaQueryList1 = window.matchMedia(mediaQuery_1);
if (mediaQueryList1.matches) {
carouselFunc(); // dynamic loaded pictures
LeftArrow.classList.add("displayNone");
let indexOfImg = 0; //to be accessed for the carousel, index for first image
let endofImg = 3; //carousel use, index for next image to be displayed
otherCarouselLogic(indexOfImg, endofImg);
if (carouselPics.length > 3) {
for (i = 3; i < carouselPics.length; i++) {
carouselPics[i].classList.add("displayNone");
}
RightArrow.classList.remove("displayNone");
} else {
RightArrow.classList.add("displayNone");
}
}
else {
carouselFunc(); //dynamic loaded Pictures
LeftArrow.classList.add("displayNone");
let IndexOfImg = 0; //to be accessed for the carousel, index for first image
let EndOfImg = 4; //carousel use, index for next image to be displayed
otherCarouselLogic(IndexOfImg, EndOfImg);
if (carouselPics.length > 3) {
for (i = 4; i < carouselPics.length; i++) {
carouselPics[i].classList.add("displayNone");
}
}
// not to display carousel arrows if pics can be showed at once
if (carouselPics.length <= 4) {
RightArrow.classList.add("displayNone");
}
}
// add event Listener
mediaQueryList1.addEventListener("change", function carouselUx(event) {
if(event.matches) {
document.getElementById("carous").innerHTML = "";
carouselFunc(); //contains dynamic loaded pictures
LeftArrow.classList.add("displayNone");
let indexOfImg = 0; //to be accessed for the carousel, index for first image
let endofImg = 3; //carousel use, index for next image to be displayed
otherCarouselLogic(indexOfImg, endofImg);
if (carouselPics.length > 3) {
for (i = 3; i < carouselPics.length; i++) {
carouselPics[i].classList.add("displayNone");
}
RightArrow.classList.remove("displayNone");
} else {
RightArrow.classList.add("displayNone");
}
}
else {
document.getElementById("carous").innerHTML = "";
carouselFunc();
LeftArrow.classList.add("displayNone");
let IndexOfImg = 0; //to be accessed for the carousel, index for first image
let EndOfImg = 4; //carousel use, index for next image to be displayed
otherCarouselLogic(IndexOfImg, EndOfImg);
if (carouselPics.length > 3) {
for (i = 4; i < carouselPics.length; i++) {
carouselPics[i].classList.add("displayNone");
}
}
// not to display carousel arrows if pics can be showed at once
if (carouselPics.length <= 4) {
RightArrow.classList.add("displayNone");
}
}
})
function otherCarouselLogic(cpF, newImg) {
/* carousel code ends here */
carouselBtn.forEach(function (btns) {
btns.addEventListener("click", function (e) {
clickedArrow = e.currentTarget.id;
if (clickedArrow === "right") {
console.log(newImg);
carouselPics[cpF].classList.add("displayNone");
carouselPics[newImg].classList.remove("displayNone");
LeftArrow.classList.remove("displayNone");
cpF += 1;
newImg += 1;
}
if (clickedArrow === "left") {
cpF -= 1;
newImg -= 1;
carouselPics[newImg].classList.add("displayNone");
carouselPics[cpF].classList.remove("displayNone");
RightArrow.classList.remove("displayNone");
console.log(newImg);
}
// ux purpose, when we get to the last pics in the carousel, the right arrow is hidden
diffR = carouselPics.length - newImg;
// diffL
if (diffR === 0) {
RightArrow.classList.add("displayNone");
}
if (cpF === 0) {
LeftArrow.classList.add("displayNone");
}
});
});
/* carousel code ends here */
}
upon load,
newImg is set to 4 and all seems to work
after resize, it is set to either 3 or 4 depending on the logic, but instead of incrementing that value alone, it gives value for the load and also for the resize.

How to add window event to close an open nav dropdown, but keep toggle open/close nav dropdown behavior on nav-item?

I have worked on this for a couple of days and I feel like I am almost there. I inherited a navigation component that behaves like any navigation -- User clicks a nav item to show the dropdown menu and clicks the nav-item or dropdown menu to close it. HOWEVER, I now need to be able to close any open dropdown menu when the user clicks anywhere else on the page.
The issue I'm facing is that I need to get my window event to ignore the nav. Otherwise, clicking the window removes the users' ability to close the nav on nav-click.
I have pasted my JavaScript code below. Can someone please assist me? Thank you.
The navClick() function was already there. I have added closeMenuOnWindowClick().
function initNav() {
const primaryNavElement = document.querySelector("#dcom-nav-primary");
const secondaryNavElement = document.querySelector("#dcom-nav-secondary");
const secondaryNavLinks = document.querySelectorAll(".dcom-nav-secondary-links");
//These variables are defined in the markup and dumped at that level. Added eslint lines below so it doesn't scream at us.
// eslint-disable-next-line
let navigationArray = [];
if(primaryNavElement){
navigationArray = primaryNavElement.getElementsByTagName("li");
}
let i;
const navClick = function() {
let navElm = this;
//Navigation buttons are relying on the navlevel attribute in the markup to determine how they behave
if(navElm,navigationArray.length,secondaryNavLinks){
if(navElm.attributes.navlevel){
if(navElm.attributes.navlevel.nodeValue == 'primary'){
//Primary Nav Click
//Reset and apply active link tab
for(i = 0; i<navigationArray.length; i++){
navigationArray[i].desktopDOMs[0].classList.remove('dcom-c-navigation-bar__li--active');
navigationArray[i].desktopDOMs[1].classList.add('dcom-c-navigation-bar__secondary-row--hidden');
}
navigationArray[navElm.index].desktopDOMs[0].classList.add('dcom-c-navigation-bar__li--active');
navigationArray[navElm.index].desktopDOMs[1].classList.remove('dcom-c-navigation-bar__secondary-row--hidden');
} else if(navElm.attributes.navlevel.nodeValue == 'secondary'){
//Secondary Nav Click
let dropdown = navElm.querySelector(".dcom-c-navigation-bar__dropdown");
if(dropdown){
for(i = 0; i<secondaryNavLinks.length; i++){
for(let ii = 0; ii<secondaryNavLinks[i].children.length; ii++){
let sDropdown = secondaryNavLinks[i].children[ii].querySelector(".dcom-c-navigation-bar__dropdown");
if(sDropdown != dropdown){
sDropdown.classList.add('dcom-c-navigation-bar__dropdown--hidden');
}
}
}
if(dropdown.children.length > 0){ //In the event the secondary dropdown has no tertiary links, do nothing, let it navigate.
// If there are only 2 items, remove flex-wrap
if(dropdown.children.length < 3) {
dropdown.style.flexWrap = 'nowrap';
}
if(dropdown.classList.contains('dcom-c-navigation-bar__dropdown--hidden')) {
dropdown.classList.remove('dcom-c-navigation-bar__dropdown--hidden');
dropdown.style.width = "auto"; //Reset width
//Handle dropdown width
//First, find out how many columns we need;
let dropdownCols = [[],[],[]];
let c = 0;
let r = 0;
for(i = 0; i < dropdown.children.length; i++){
dropdownCols[c].push(dropdown.children[i]);
r++;
if(r > 5){
r = 0;
c++;
}
}
//Then step through the links we have, find the largest, and set the largest size in each column
let colWidths = [0,0,0];
for(i = 0; i<dropdownCols.length; i++){
for(let ii = 0; ii<dropdownCols[i].length; ii++){
let linkWidth = dropdownCols[i][ii].offsetWidth;
if(linkWidth > colWidths[i])colWidths[i]=linkWidth;
}
}
//Set the width of the dropdown to the size of the largest columns
dropdown.style.width = (colWidths[0]+colWidths[1]+colWidths[2]+20)+"px";
//For IE fix we have to manually set the height. It's stupid, I know.
let linkHeight = dropdownCols[0][0].offsetHeight + 3; //get the height of one link element
//Check if its less than 6 (which wraps)
if(dropdown.children.length < 6){
//If its less than 6, make the height the height of the number of children
dropdown.style.height = (linkHeight * dropdown.children.length)+"px";
} else {
//Otherwise, set it to 6
dropdown.style.height = linkHeight * 6+"px";
}
//Check if this div is going off the screen
dropdown.style.right = "";
dropdown.style.left = "";
var dropdownRect = dropdown.getBoundingClientRect();
if((dropdownRect.left + dropdown.offsetWidth) > window.innerWidth){
dropdown.style.right = 0;
dropdown.style.left = "auto";
}
} else {
dropdown.classList.add('dcom-c-navigation-bar__dropdown--hidden');
//hide it if its open
}
}
}
}
/* Note: Since we don't have a Single Page Application here (haha), the assumption is that tertiary links will cause a page reload/navigation.
* In the event this is used in a SPA framework, can add a check here for tertiary controls, and add a click handler above.
*/
} else {
//Otherwise, using a switch based on the element id (for now! have had success with custom attributes before to determine button purpose that has less risk of conflict)
switch(navElm.id) {
default:
//Do nothing
}
}
}
};
if(primaryNavElement,secondaryNavElement,secondaryNavLinks.length){ //check all elements exist
//Find and add click handlers to all primary nav links
for(i = 0; i<primaryNavElement.children.length; i++){
if(primaryNavElement.children[i].nodeName == "LI"){ //double checking I'm grabbing list items
navigationArray[i].desktopDOMs = [];
navigationArray[i].desktopDOMs.push(primaryNavElement.children[i]); //Assign to reference array for future use
primaryNavElement.children[i].index = i; //I add an index here for reference
primaryNavElement.children[i].addEventListener('click', navClick);
//each primary nav's secondary navs
}
}
//Find all secondary links bars, store reference
for(i = 0; i<secondaryNavElement.children.length; i++){
navigationArray[i].desktopDOMs.push(secondaryNavElement.children[i]);
secondaryNavElement.children[i].index = i;
//secondaryNavElement.children[i]
}
//For all links, add handler, push to desktopDOMS
//console.log(secondaryNavLinks);
for(i = 0; i<secondaryNavLinks.length; i++){
for(let ii = 0; ii<secondaryNavLinks[i].children.length; ii++){
secondaryNavLinks[i].children[ii].index = i;
secondaryNavLinks[i].children[ii].addEventListener('click', navClick);
}
}
}
function closeMenuOnWindowClick() {
//Get all dropdowns
let menus = document.getElementsByClassName('dcom-c-navigation-bar__dropdown');
//Convert to array
menus = [...menus];
let openMenu;
//Loop through to find secondary level menu that is currently open
menus.forEach(menu => {
//If it's a secondary level nav and it's showing
if(menu.attributes.navlevel = 'secondary' && !menu.classList.contains('dcom-c-navigation-bar__dropdown--hidden')){
//Get it and label it
openMenu = menu;
openMenu.classList.add('dcom-c-navigation-bar__dropdown--hidden');
}
})
}
window.addEventListener('mouseup', function() {
closeMenuOnWindowClick();
navClick();
}, false);
}
module.exports = initNav;
Thanks to stackoverflow, I found bits of snippets here and there and was able to succeed!
Here's how I accomplished the solution:
// Click anywhere on the page to close an open menu.
window.addEventListener('click', function (ev) {
// Click anywhere, excluding element with a class you want to ignore, to hide the dropdown.
if (ev.target.className !== 'ignoreThisClass') {
dropdown.classList.add('addYourOwnHideClass');
}
});

How do I iterate through a loop 1 by 1 removing a classList value each time an event is fired?

I have a function attached to a scroll-down event which adds a class one by one to an array of items. I want to now make a function that does the opposite and attach it to a scroll up event. Any help will be met with great praise!
I made a function that when the scroll up event is fired it removes all the classes from the array, but I want to remove them one by one.
var slide = document.querySelectorAll('div.about-slide');
This is the function im using to add the classes to the elements one by one
function addOnScroll() {
for ( var i = 0; i < slide.length; i++){
if(slide[i].classList.contains('scroll')){
continue;
}else{
slide[i].classList.add('scroll');
break;
}
}
};
This is fired on a scroll up event, it removes all the added class from the code above. But I want it to remove the classes one by one.
function removeOnScroll() {
for ( var i = 0; i < slide.length; i++){
slide[i].classList.remove('scroll')
}
};
The mouse scroll event I'm using.
function onDocumentMouseScroll( event ) {
console.log(event);
if( Date.now() - lastMouseWheelStep > 600 ) {
lastMouseWheelStep = Date.now();
var delta = event.detail || -event.wheelDelta;
if( delta > 0 ) {
navigateNext();
addOnScroll();
moveBackground();
}
else if( delta < 0 ) {
navigatePrev();
removeOnScroll();
}
}
}
So, why not do the opposite of what you do on the scroll down ?
function addOnScroll() {
for (var i = 0; i < slide.length; i++) {
if (slide[i].classList.contains('scroll')) {
continue;
} else {
slide[i].classList.add('scroll');
break;
}
}
};
function removeOnScroll() {
for (var i = slide.length - 1; i >= 0; i--) {
if (!slide[i].classList.contains('scroll')) {
continue;
} else {
slide[i].classList.remove('scroll');
break;
}
}
};
However, to simplify your code you could just do a new selection each time
function addOnScroll() {
var next = document.querySelector('div.about-slide:not(.scroll)');
if (next)
next.classList.add('scroll');
};
function removeOnScroll() {
var all = document.querySelectorAll('div.about-slide.scroll'),
last = all[all.length - 1]
if (last)
last.classList.remove('scroll');
};
You can improve below code and it will help updating class one by one Fiddle.
// For Chrome
window.addEventListener('mousewheel', mouseWheelEvent);
// For Firefox
window.addEventListener('DOMMouseScroll', mouseWheelEvent);
function mouseWheelEvent(e) {
var slide = document.getElementsByClassName("about-slide");
var bScroll = document.documentElement.scrollTop;
for (var i = 0; i < slide.length; i++) {
var sHeight = slide[i].offsetHeight;
var offsets = slide[i].offsetTop;
if (bScroll > offsets && bScroll < offsets + sHeight) {
slide[i].classList.add('scroll');
} else {
slide[i].classList.remove('scroll');
}
}
}

Setting data type value to an element when it reaches the top of the viewport

I have a hex colour value stored on each section and when a section reaches the top of the screen (-180px for the header) I want to assign a css property to the header element in order to change the text colour as you scroll through the sections. I am not getting any errors and I am having trouble debugging this issue.
http://www.amypreston.co.uk/
$(window).load(function() {
var $header = $("header");
var numberOfSections = $("section").length;
var sectionOffsets = [];
var sectionColour = $("section").eq(i).data("colour");
for(var i = 0; i < numberOfSections; i++) {
sectionOffsets.push($("section").eq(i).offset().top);
}
$(window).scroll(function() {
var scrollTop = $(this).scrollTop();
for(var i = 0; i < numberOfSections; i++) {
if(scrollTop > sectionOffsets[i] - 180) {
$header.css('color', 'sectionColour');
}
}
});
});
I dont know if it happened by accident, but the line
var sectionColour = $("section").eq(i).data("colour");
is out of place. it uses a variable i which is only defined in the window scroll handler.
notice that you need to retrieve the section color each time the scroll handler runs, and not only on window load. you need to place this line from above in the loop inside the scroll handler.
Plus, as stated on the comments, you need to use the sectionColour as a variable, and not as a string like you do now. the single quote marks must be removed, so 'sectionColour' turns into sectionColour.
here is your fixed code:
$(window).load(function() {
var $header = $("header");
var numberOfSections = $("section").length;
var sectionOffsets = [];
for(var i = 0; i < numberOfSections; i++) {
sectionOffsets.push($("section").eq(i).offset().top);
}
$(window).scroll(function() {
var scrollTop = $(this).scrollTop();
for(var i = 0; i < numberOfSections; i++) {
if(scrollTop > sectionOffsets[i] - 180) {
var sectionColour = $("section").eq(i).data("colour");
$header.css('color', sectionColour);
}
}
});
});
On a side note, you could shorten your code into this:
$(window).scroll(function () {
$("section").each(function () {
if ($(window).scrollTop() > $(this).offset().top - 180) {
$("header").css('color', $(this).data("colour"));
}
});
}).scroll();

Responsive jQuery menu, collapsing last elements into separate menu

I have a single level navigation list. What I want to do is, on window resize, add a dropdown at the end to display elements that do not fit the window width.
Below an image representation:
var base = this,
container = $(base.selector),
items = container.find('>li:not(.smallmenu):visible'),
count = container.find('>li:not(.smallmenu):visible').length,
listWidth = [],
added;
items.each(function() {
listWidth.push($(this).width());
});
function getWidth() {
var width = 0;
container.find('>li:not(.smallmenu):visible').each(function() {
width += $(this).outerWidth();
});
return width + 100;
}
function hideLast() {
var i = container.find('>li:not(.smallmenu):visible').last().index()
if( $(window).width() < (getWidth()) ) {
items.eq(i).hide();
if(!added) {
$('<li class="smallmenu"><i class="fa fa-plus"></i></li>').appendTo(container);
added = true;
}
}
}
function showLast() {
var i = container.find('>li:not(.smallmenu):visible').last().index();
if( (getWidth() + listWidth[i+1]) < $(window).width() ) {
container.find('>li:not(.smallmenu)').eq(i+1).show();
if((i+2) === count) {
container.find('.smallmenu').remove();
added = false;
}
}
}
$(window).resize(function() {
showLast();
hideLast();
});
However this is not working as expected and half-done. I feel like I am heading in the wrong direction.
EDIT: Here is an updated jsFiddle: http://jsfiddle.net/anteksiler/zyv8f/1/ Resize your browser to get the effect.

Categories

Resources