updating function when nav link is clicked - javascript

Hello I have a feature where when you click a link in the navigation the content of the body switches and I was combining it with a little script from this question and it works great. The only thing is that if you click a link and don't scroll the images won't show up unless you scroll at least 1px. So I was wondering if there's a fix for this. JSFiddle.
Side question: Would it be possible to wait for the content to show up until the page is fully scrolled up?
Script:
$(window).on("load",function() {
function fade(pageLoad) {
var windowTop=$(window).scrollTop(), windowBottom=windowTop+$(window).innerHeight();
var min=0, max=1, threshold=0.01;
$(".fade").each(function() {
/* Check the location of each desired element */
var objectHeight=$(this).outerHeight(), objectTop=$(this).offset().top, objectBottom=$(this).offset().top+objectHeight;
/* Fade element in/out based on its visible percentage */
if (objectTop < windowTop) {
if (objectBottom > windowTop) {$(this).fadeTo(0,min+((max-min)*((objectBottom-windowTop)/objectHeight)));}
else if ($(this).css("opacity")>=min+threshold || pageLoad) {$(this).fadeTo(0,min);}
} else if (objectBottom > windowBottom) {
if (objectTop < windowBottom) {$(this).fadeTo(0,min+((max-min)*((windowBottom-objectTop)/objectHeight)));}
else if ($(this).css("opacity")>=min+threshold || pageLoad) {$(this).fadeTo(0,min);}
} else if ($(this).css("opacity")<=max-threshold || pageLoad) {$(this).fadeTo(0,max);}
});
} fade(true); //fade elements on page-load
$(window).scroll(function(){fade(false);}); //fade elements on scroll
});

You can achieve it by taking fade method outside the load event listener that will make it available everywhere. And then call it from on the click of nav buttons.
See the js below:
$(window).on("load",function() {
this.fade(true); //fade elements on page-load
$(window).scroll(function(){this.fade(false);}); //fade elements on scroll
});
//Take below fade method outside of the load event listener.
function fade(pageLoad) {
var windowTop=$(window).scrollTop(), windowBottom=windowTop+$(window).innerHeight();
var min=0, max=1, threshold=0.01;
$(".fade").each(function() {
/* Check the location of each desired element */
var objectHeight=$(this).outerHeight(), objectTop=$(this).offset().top, objectBottom=$(this).offset().top+objectHeight;
/* Fade element in/out based on its visible percentage */
if (objectTop < windowTop) {
if (objectBottom > windowTop) {$(this).fadeTo(0,min+((max-min)*((objectBottom-windowTop)/objectHeight)));}
else if ($(this).css("opacity")>=min+threshold || pageLoad) {$(this).fadeTo(0,min);}
} else if (objectBottom > windowBottom) {
if (objectTop < windowBottom) {$(this).fadeTo(0,min+((max-min)*((windowBottom-objectTop)/objectHeight)));}
else if ($(this).css("opacity")>=min+threshold || pageLoad) {$(this).fadeTo(0,min);}
} else if ($(this).css("opacity")<=max-threshold || pageLoad) {$(this).fadeTo(0,max);}
});
}
// change activenav class, show the clicked element only and hide the others https://codepen.io/MohdHussein/pen/MWKEvdp
// grab all the buttons
let Buttons = document.querySelectorAll(".selectSection button");
// loop through the buttons using for..of
for (let button of Buttons) {
// listen for a click event
button.addEventListener('click', (e) => {
// et = event target
const et = e.target;
// slect activenav class
const activenav = document.querySelector(".activenav");
// check for the button that has activenav class and remove it
if (activenav) {
activenav.classList.remove("activenav");
}
// add activenav class to the clicked element
et.classList.add("activenav");
// select all classes with the name content
let allContent = document.querySelectorAll('.contentsec');
// loop through all content classes
for (let contentsec of allContent) {
// display the content if the class has the same data-attribute as the button
if (contentsec.getAttribute('data-number') === button.getAttribute('data-number')) {
contentsec.style.display = "block";
}
// if it's not equal then hide it.
else {
contentsec.style.display = "none";
}
}
this.fade(true); //Call fade method on click
});
}
You can test it here.

Related

Remove inline styles when clicking on button

The following happens to me:
I have made a slider with the next and previous arrows that works correctly. The case is that I have a button called "see all" in which when I press it, it puts a display none to the previous and next arrows and shows everything that is in the slider (that is to say, when I press the button, it shows everything and "deactivates" the slider).
The problem is that when you press the button again to stop displaying everything and return to "slider mode", the previous and next buttons do not go through the slider.
The slider is putting the active class and removing it to show what is inside. But once I have used the button of "see all" it is added in line a "display: none" and although I give to the arrows of previous or following and the class active is put correctly it remains the style="display:none;" inline in the html and it stops working.
The code of the button that shows everything:
var btnMore = document.querySelector(".btn-panes");
var boxes = document.querySelectorAll("#slider .box-panes");
var aLeft = document.querySelector("#slider .left");
var aRight = document.querySelector("#slider .right");
var btnMoreActivated = false;
btnMore.addEventListener("click", function(){
if(!btnMoreActivated){
for(var i=0; i<boxes.length; i++ ){
document.querySelector("#slider").style.flexWrap = "wrap";
boxes[i].style.display = "flex";
aLeft.classList.add("ocultar");
aRight.classList.add("ocultar");
}
btnMoreActivated = true;
} else {
for(var i=1; i<boxes.length; i++ ){
boxes[i].style.display = "none";
document.querySelector("#slider").style.flexFlow = "nowrap";
aLeft.classList.remove("ocultar");
aRight.classList.remove("ocultar");
}
btnMoreActivated = false;
}
});
The slider code (it works) but just to show you what it does in case you need to add something to fix the problem:
const items = document.querySelectorAll('#slider .box-panes');
const itemCount = items.length;
const nextItem = document.querySelectorAll('.right img');
const previousItem = document.querySelectorAll('.left img');
var count = 0;
function shorHide(){
switch (key) {
case value:
break;
default:
break;
}
}
function showNextItem() {
items[count].classList.remove('active');
if(count < itemCount - 1) {
count++;
} else {
count = 0;
}
items[count].classList.add('active');
}
function showPreviousItem() {
items[count].classList.remove('active');
if(count > 0) {
count--;
} else {
count = itemCount - 1;
}
items[count].classList.add('active');
}
function keyPress(e) {
e = e || window.event;
if (e.keyCode == '37') {
showPreviousItem();
} else if (e.keyCode == '39') {
showNextItem();
}
}
nextItem[0].addEventListener('click', showNextItem);
previousItem[0].addEventListener('click', showPreviousItem);
document.addEventListener('keydown', keyPress);
This is because inline styles are the most specific type of style to add and therefore the most difficult to override. The simplest solution is to not use inline styles and instead use a class that can be added or removed as needed.
Here's a simple example that you can use to replace: boxes[i].style.display = "none";
// Get the elements that need to be hidden/shown into a collection
let btns = document.querySelectorAll(".nav");
document.getElementById("hide").addEventListener("click", function(e){
// Loop over the collection
btns.forEach(function(el){
el.classList.add("hidden"); // Apply the hidden class
});
});
document.getElementById("show").addEventListener("click", function(e){
btns.forEach(function(el){
el.classList.remove("hidden"); // Remove the class
});
});
.hidden { display:none; }
<button id="hide">Hide Buttons</button>
<button id="show">Show Buttons</button>
<button class="nav"><<</button> <button class="nav">>></button>

Web accessible navigation

I'm stuck while implementing drop down for each individual list item on focus/hover on any of them, now on hovering over any single list item all dropdowns are getting displayed.
This is my pen: https://codepen.io/apeandme/pen/GRZOxJQ
and the JS:
// main navigation interaction
'use strict';
var navbar;
var triggerContainer;
var triggerLink;
var toggleButton;
var lastMenuItem;
var mouseOutTimer; // timer used to delay hiding of menu after mouse leaves
var mouseOutHideDelay = 0; // time (in ms) before menu is closed after mouse leaves
var menuVisible = false;
var i = 0;
window.addEventListener('DOMContentLoaded', function(e) {
navbar = document.querySelector('nav');
triggerContainer = document.querySelectorAll('nav > ul > li.with-dd');
triggerLink = document.querySelectorAll('nav > ul > li.with-dd > a');
toggleButton = document.querySelectorAll('nav > ul > li.with-dd > .toggle-button');
lastMenuItem = document.querySelectorAll('nav > ul > li.with-dd > ul > li:last-of-type > a');
// Show the menu on mouse hover of the trigger
triggerContainer.forEach(item => {
item.addEventListener('mouseover', function(e) {
showMenu();
clearTimeout(mouseOutTimer);
});
});
// Hide the menu when mouse hover leaves both the trigger and menu fly-out, but only after a short delay to help people jerky mouse movements (like those using head/eye trackers)
triggerContainer.forEach(item => {
item.addEventListener('mouseout', function(e) {
mouseOutTimer = setTimeout(function() {
hideMenu();
}, mouseOutHideDelay);
});
});
// Hide the menu when the user tabs out of it
triggerContainer.forEach(item => {
item.addEventListener('keydown', triggerKeydownHandler);
});
// Toggle the menu when the trigger is activated
toggleButton.forEach(item => {
item.addEventListener('click', toggleMenu);
});
// Close the menu when the user activates something outside the navbar.
document.body.addEventListener('click', handleBodyClick);
});
/**
Menu visibility
**/
function showMenu() {
triggerLink.forEach(item => {
item.setAttribute('aria-expanded', true);
});
toggleButton.forEach(item => {
item.setAttribute('aria-expanded', true);
});
menuVisible = true;
}
function hideMenu() {
triggerLink.forEach(item => {
item.setAttribute('aria-expanded', false);
});
toggleButton.forEach(item => {
item.setAttribute('aria-expanded', false);
});
menuVisible = false;
}
function toggleMenu() {
if (menuVisible) {
hideMenu();
} else {
showMenu();
}
}
/**
Event handlers
*/
function handleBodyClick(e) {
if (!navbar.contains(e.target)) {
hideMenu();
}
}
function triggerKeydownHandler(e) {
// Hide the menu a keyboard user tabs out of it or presses Escape
if ((e.key === 'Tab' && !e.shiftKey && e.target === lastMenuItem) || e.key == 'Escape') {
hideMenu();
// Move focus back to the menu toggle button if Escape was pressed
if (e.key == 'Escape') {
toggleButton.focus();
}
}
}
Instead of opening all the dropdowns at once when one is hovered, you need to pass the 'hover' event to the function, in order to know which one of the triggers were hovered :
triggerContainer.forEach((item) => {
item.addEventListener("mouseover", function (event) { // catch the 'hover' event
showMenu(event); // pass the event to your function
clearTimeout(mouseOutTimer);
});
});
function showMenu(event) {
event.target.setAttribute("aria-expanded", true); // know which element whas hovered
event.target.nextElementSibling.setAttribute("aria-expanded", true); // open the corresponding dropdown
menuVisible = true;
}
Thanks for the help. Have changed the code , for both showMenu and hideMenu and for mouseover and mouseout events, now individual drop downs are showing on hovering over single menu item, but the dropdowns are hiding soon enough I'm reaching to the last menu item inside the dropdown. Am I missing anything and currently this error is in the console whenever hovering over the last menu item inside the drop-down-
Uncaught TypeError: Cannot read property 'setAttribute' of null
at hideMenu (main.js:68)
at main.js:38
//main navigation interaction
"use strict";
var navbar;
var triggerContainer;
var triggerLink;
var toggleButton;
var lastMenuItem;
var mouseOutTimer; // timer used to delay hiding of menu after mouse leaves
var mouseOutHideDelay = 1000; // time (in ms) before menu is closed after mouse leaves
var menuVisible = false;
window.addEventListener("DOMContentLoaded", function (e) {
navbar = document.querySelector("nav");
triggerContainer = document.querySelectorAll("nav > ul > li.with-dd");
triggerLink = document.querySelectorAll("nav > ul > li.with-dd > a");
toggleButton = document.querySelectorAll(
"nav > ul > li.with-dd > .toggle-button"
);
lastMenuItem = document.querySelectorAll(
"nav > ul > li.with-dd > ul > li:last-of-type > a"
);
// Show the menu on mouse hover of the trigger
triggerContainer.forEach((item) => {
item.addEventListener("mouseover", function (e) {
showMenu(e);
clearTimeout(mouseOutTimer);
});
});
// Hide the menu when mouse hover leaves both the trigger and menu fly-out, but only after a short delay to help people jerky mouse movements (like those using head/eye trackers)
triggerContainer.forEach((item) => {
item.addEventListener("mouseout", function (e) {
mouseOutTimer = setTimeout(function () {
hideMenu(e);
}, mouseOutHideDelay);
});
});
// Hide the menu when the user tabs out of it
triggerContainer.forEach((item) => {
item.addEventListener("keydown", triggerKeydownHandler);
});
// Toggle the menu when the trigger is activated
toggleButton.forEach((item) => {
item.addEventListener("click", toggleMenu);
});
// Close the menu when the user activates something outside the navbar.
document.body.addEventListener("click", handleBodyClick);
});
/**
Menu visibility
**/
function showMenu(e) {
e.target.setAttribute("aria-expanded", true);
e.target.nextElementSibling.setAttribute("aria-expanded", true);
menuVisible = true;
}
function hideMenu(e) {
e.target.setAttribute("aria-expanded", false);
e.target.nextElementSibling.setAttribute("aria-expanded", false);
menuVisible = false;
}
function toggleMenu() {
if (menuVisible) {
hideMenu(e);
} else {
showMenu(e);
}
}
/**
Event handlers
*/
function handleBodyClick(e) {
if (!navbar.contains(e.target)) {
hideMenu();
}
}
function triggerKeydownHandler(e) {
// Hide the menu a keyboard user tabs out of it or presses Escape
if (
(e.key === "Tab" && !e.shiftKey && e.target === lastMenuItem) ||
e.key == "Escape"
) {
hideMenu();
// Move focus back to the menu toggle button if Escape was pressed
if (e.key == "Escape") {
toggleButton.focus();
}
}
}

Closing toggle menu not working properly with JQuery code

I am working on closing toggle menu for mobiles and having a small problem. So what i want is when the toggle menu is active, user to be able to close it by touching somewhere on the screen on his device. I almost got it working, but when closed the basket in the header disappears and the menu doesn't retrieve to a hamburger icon. I am working on Wordpress website, just to notice.
I guess the problem comes from this: aria-expanded="true" , because the default value should be false after the user has closed it.
So my website is:
https://www.ngraveme.com/bg
my JQuery code is:
jQuery(document).ready(function($) {
var $menu = $('.menu');
$('.menu-toggle').click(function() {
$menu.toggle();
});
$(document).mouseup(function(e) {
if (!$menu.is(e.target) // if the target of the click isn't the container...
&&
$menu.has(e.target).length === 0) // ... nor a descendant of the container
{
$menu.hide();
}
});
});
and the original js code written from the theme i am using in wordpress is:
/**
* navigation.js
*
* Handles toggling the navigation menu for small screens.
* Also adds a focus class to parent li's for accessibility.
* Finally adds a class required to reveal the search in the handheld footer bar.
*/
(function() {
// Wait for DOM to be ready.
document.addEventListener('DOMContentLoaded', function() {
var container = document.getElementById('site-navigation');
if (!container) {
return;
}
var button = container.querySelector('button');
if (!button) {
return;
}
var menu = container.querySelector('ul');
// Hide menu toggle button if menu is empty and return early.
if (!menu) {
button.style.display = 'none';
return;
}
button.setAttribute('aria-expanded', 'false');
menu.setAttribute('aria-expanded', 'false');
menu.classList.add('nav-menu');
button.addEventListener('click', function() {
container.classList.toggle('toggled');
var expanded = container.classList.contains('toggled') ? 'true' : 'false';
button.setAttribute('aria-expanded', expanded);
menu.setAttribute('aria-expanded', expanded);
});
// Add class to footer search when clicked.
document.querySelectorAll('.storefront-handheld-footer-bar .search > a').forEach(function(anchor) {
anchor.addEventListener('click', function(event) {
anchor.parentElement.classList.toggle('active');
event.preventDefault();
});
});
// Add focus class to parents of sub-menu anchors.
document.querySelectorAll('.site-header .menu-item > a, .site-header .page_item > a, .site-header-cart a').forEach(function(anchor) {
var li = anchor.parentNode;
anchor.addEventListener('focus', function() {
li.classList.add('focus');
});
anchor.addEventListener('blur', function() {
li.classList.remove('focus');
});
});
// Add an identifying class to dropdowns when on a touch device
// This is required to switch the dropdown hiding method from a negative `left` value to `display: none`.
if (('ontouchstart' in window || navigator.maxTouchPoints) && window.innerWidth > 767) {
document.querySelectorAll('.site-header ul ul, .site-header-cart .widget_shopping_cart').forEach(function(element) {
element.classList.add('sub-menu--is-touch-device');
});
}
});
})();
Try replacing your jQuery code with this:
jQuery(document).ready(function($) {
$(document).mouseup(function(e) {
var $menuContainer = $('.menu');
var $menu = $menu.find('ul');
var $container = $('.site-navigation');
var $button = $container.find('button')
if (!$menuContainer.is(e.target) && $menuContainer.has(e.target).length === 0) {
if ($container.hasClass('toggled')) {
$button.attr('aria-expanded', false);
$menu.attr('aria-expanded', false);
}
}
});
});
It uses the vanilla-js code from the template for hiding/showing the menu, but with jQuery synthax.

Highlighting active tab after overriding the active class (jQuery)

I am having issues highlighting the active tab of a navbar (bootstrap)
The page works like so:
User Adds a product
Once added a new tab will appear with the products name shown
The user can then click on that tab to checkoff checkboxes belonging to that product
Keep adding products and/or deleting products
During step 3 the tab color is updated to match the color of the progress bar within the tab.
My problem is that I want the tab to have a different color when it is selected (active) and then revert back to the progress bar color once another tab is clicked.
Code:
$(document).ready(function () {
$('#new').keypress(function (e) {
var key = e.which;
if (key == 13) {
AddTabFromTemplate($('#newProductName').val());
}
})
$('#btnAddNew').on('click', function () {
AddTabFromTemplate($('#newProductName').val());
});
// Listen for all js-deletetab in the context of #tab-content, even if the tab panels are dynamically generated
$('#tab-content').on('click', '.js-deletetab', function () {
// Find the parent tab-pane's ID and call the Remove Tab function
RemoveTab($(this).parents('.tab-pane').attr('id'));
});
});
function AddTabFromTemplate(newID) {
// Sanitize ID in here is fine
var domID = newID.replace(/ /g, '_');
// Make a copy of the template
$newTab = $('#template').clone();
$newTab.attr('id', domID);
// Insert the new tab page right before our "new" tab.
$newTab.insertBefore('#new');
// Create the tab item, too
var tab = $('<li role="presentation">' + newID + '</li>').insertBefore('#newTab');
tab = tab.find('a');
//Update ProgressBar
ProgressBar($newTab, tab);
}
//Uses the newly created tab to target its progress bar
function ProgressBar( currentTab, liTab){
//Grab all the checkboxes
var checkboxes = currentTab.find('.checkbox');
liTab.css({
'background-color': '#D9534F',// red = #D9534F;
'color': 'white'
});
//Grab the Landing required checkboxes
var landDisp = currentTab.find('.land-display');
var landingPage = currentTab.find('.req');
var landReq = false;
landDisp.css('display', 'none');
//If landing page is required display the rest of the checkboxes
// and updated the landReq bool
landingPage.on('click', function(){
if(landReq === false){
landDisp.toggle();
landReq = true;
}
else if(landReq === true){
landDisp.toggle();
landReq = false;
}
})
checkboxes.on('click', function(){
var emptyValue = 0;
//Checks each checkbox in the tab for checked or not checked
checkboxes.each(function() {
if($(this).is(':checked')){
//If the landing required checkbox is checked update
// the value of the emptyValue for progress bar
if(landReq === false){
emptyValue += 5.3;
}
else if(landReq === true){
emptyValue += 4.4;
}
}
});
//Progressbar update section
if(emptyValue > 30 && emptyValue < 70 ){
currentTab.find('.progress-bar').removeClass('progress-bar-danger');
currentTab.find('.progress-bar').addClass('progress-bar-warning');
liTab.css('background-color', '#F0AD4E');// yellow = #F0AD4E;
}
else if(emptyValue >= 70){
currentTab.find('.progress-bar').removeClass('progress-bar-warning');
currentTab.find('.progress-bar').addClass('progress-bar-success');
liTab.css('background-color', '#5CB85C');// green = #5CB85C;
}
currentTab.find('.progress-bar').css('width', emptyValue + '%').attr('aria-valuenow', emptyValue);
});
}
Codepen
Any help is appreciated.
You can add
li.active a {
background-color: black !important;
}
Here is a working codepen.
The background color for the a tag will be black as long as the li has the active class. Using the !important we overrider the inline-style definition of that a tag.

Scroll position based javascript animation does not revert to it's original state when scrolling back up

See the JSFiddle here: http://jsfiddle.net/jL6d2qp6/
I have an animation that is supposed to keep the #top element in a fixed position at the top of the page, except for when the #login element is on the screen. To control this, I am using a javascript function that runs every 10ms and switches out the css class for #top, and when I scroll down, it updates as expected, but when I try to scroll back up, nothing happens.
javascript code in question:
offScreen = function(id, targetValue)
{
var offset = $("#top").offset();
var w = $(window);
var height = $(id).innerHeight();
var finalOffset = (offset.top + height) - w.scrollTop();
if (finalOffset < targetValue)
{
return true;
}
else
{
return false;
}
}
function updateTopMenu()
{
if (offScreen("#login", 81) === false)
{
if($("#top").hasClass("top-bar-absolute") === false)
{
$("#top").addClass("top-bar-absolute");
console.log("added top-bar-absolute");
}
if($("#top").hasClass("top-bar-fixed") === true)
{
$("#top").removeClass("top-bar-fixed");
console.log("removed top-bar-fixed");
}
}
if(offScreen("#login", 81) === true)
{
if($("#top").hasClass("top-bar-absolute") === true)
{
$("#top").removeClass("top-bar-absolute");
console.log("removed top-bar-absolute");
}
if($("#top").hasClass("top-bar-fixed") === false)
{
$("#top").addClass("top-bar-fixed");
console.log("added top-bar-fixed");
}
}
}
$("#top").ready( function() {
setInterval(updateTopMenu, 10);
});
Also, if there is a better way to accomplish this, I'd like it because this feels kind of cheaty.
The easiest way to achieve this is listening to the scroll event on the window. This is called every time the user scrolls. Then you can check whether the user scrolled past the login box, i.e. beyond the login box's height.
If the login box is no longer in the window, assign the #top box a class like .sticky that will change its position to position: fixed. And otherwise remove this class.
Checkout this jsFiddle.

Categories

Resources