I have this ol with lis into it. On the click of the ol I want the ol to be hidden, but if clicked on the li it should not hide. And this should happen when the screen size is below 768px. Below is my code
jQuery('ol.main-nav').click(function () {
if (jQuery(window).width() <= 768) {
jQuery('ol.main-nav > li ').click(function (event) {
event.stopPropagation();
})
jQuery('ol.main-nav').hide(100);
}
})
Actually, my code is hiding the ol.nav-main. While using the debugger, it doesn't go into the if condition, but still somehow executes the piece of code.
Try this way:
jQuery('ol.main-nav').click(function () {
if (jQuery(window).width() <= 768) {
jQuery('ol.main-nav').hide(100);
}
});
jQuery('ol.main-nav > li ').click(function (event) {
if (jQuery(window).width() <= 768) {
event.stopPropagation();
}
})
You almost never want to hook up an event handler inside another event handler.
You have two choices for what you're describing:
Check when the event occurs to see if it travelled through an li:
jQuery('ol.main-nav').click(function(event) {
var t;
if (jQuery(window).width() <= 768) {
for (t = event.target; t != this; t = t.parentNode) {
if (t.tagName == "LI") {
return; // The click came through an LI
}
}
jQuery('ol.main-nav').hide(100);
}
});
or
Add a handler to the lis that stops propagation. It's much simpler:
jQuery('ol.main-nav').click(function () {
if (jQuery(window).width() <= 768) {
jQuery('ol.main-nav').hide(100);
}
});
jQuery('ol.main-nav > li').click(function(event) {
event.stopPropagation(); // If you like check `width` here too
});
Do a single click event, test if the clicked element is a list item or if the clicked element is a child of a list item:
jQuery('ol.main-nav').click(function (e) {
if (jQuery(window).width() <= 768 && jQuery(e.target).closest('ol.main-nav > li').length) {
e.preventDefault();
} else {
jQuery('ol.main-nav').hide(100);
}
});
demo:https://jsfiddle.net/Lm73y2bL/
Related
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();
}
}
}
I am trying to animate some divs after the user scrolls to a specific position on the page. the problem is that i want it to happen only once. I used Boolean flags but it doesn't seem to like it.
What are u all suggest me to do?
::the code Its not even running
FYI I don't want to use PHP
var once = false;
$(document).ready(function() {
if ($(window).scrollTop() > 760 && once == false) {
$('.hash').each(function(i) {
$(this).fadeOut(0).delay(1000 * i).fadeIn(1000);
});
once = true;
}
)};
Thanks!
From your question
after the user scrolls to a specific position on the page
Listen to scroll event
$(document).ready(function() {
var once = false;
$(document).on('scroll', function(){
if ($(window).scrollTop() > 760 && once==false){
$('.hash').each(function(i) {
$(this).fadeOut(0).delay(1000*i).fadeIn(1000);
});
once=true;
}
});
)};
Alternative from comments. Check if element has a class (or attribute) or not. Below code checks if the element has the data-noanimate attribute. If yes it will not animate, if not it will animate and add data-noanimate so that it will animate once.
$(document).ready(function() {
$(document).on('scroll', function(){
if ($(window).scrollTop() > 760){
$('.hash').each(function(i) {
if($(this).attr('data-noanimate') === undefined){
$(this).attr('data-noanimate','true').fadeOut(0).delay(1000*i).fadeIn(1000);
}
});
}
});
)};
var once=false;
$(document).ready(function() {
if ($(window).scrollTop() > 760 &&once==false)
{
$('.hash').each(function(i) {
$(this).fadeOut(0).delay(1000*i).fadeIn(1000);});
once=true;
}
});
Your brackets on the end of the ready function were flipped.
The other answer is correct, but it can be better like this:
$(function() {
$(window).on('scroll', function(){
if ($(window).scrollTop() > 760) {
$('.hash').each(function(i) {
$(this).fadeOut(0).delay(1000 * i).fadeIn(1000);
});
// without boolean value,you can off `scroll` event
$(window).off('scroll');
}
})
});
I currently have a sticky scroll navigation that changes the active class as it passes each section on the page. It is a little buggy though...
heres my jquery code:
var s = $("#page-nav"),
pos = s.position();
$(window).scroll(function() {
var windowpos = $(window).scrollTop();
if (windowpos >= pos.top) {
s.addClass("stick");
$('.main').css('margin-top', '60px');
} else {
s.removeClass("stick");
$('.main').removeAttr('style');
}
});
$(window).scroll(function() {
var y = $(this).scrollTop();
$('.linky').each(function (event) {
id = $(this).attr('href');
if (y >= $(id).offset().top) {
$('.linky').not(this).removeClass('active');
$(this).addClass('active');
}
});
});
//page nav
$("#page-nav li a").click(function(e) {
e.preventDefault();
$("#page-nav li a").removeClass('active');
$(this).addClass('active');
goToByScroll($(this).attr("href").replace("#", ""));
});
function goToByScroll(id){
$('html,body').animate({
scrollTop: $("#"+id).offset().top},
'slow');
}
And here is a working example over at codepen
How can I improve my javascript so that is performs a little smoother. As you can see when you click a link it still thinks it is within that section and the active class flickers.
Your problem was that you were setting the active class when you click an link and also when you move. What I did was to remove the class handling from the click event handler and let the scroll handler take care of everything. This way, there is no flicker. Here you have an updated CodePen.
$("#page-nav li a").click(function(e) {
e.preventDefault();
goToByScroll($(this).attr("href").replace("#", ""));
});
If this solution is not good enough for you, tell me and I'll think something more sophisticated.
New working solution here.
Basically, I created a variable that indicates whether you have clicked a link or not. If you have, then the scroll handler won't change CSS classes (avoiding the flicker). Then, in the complete handler of your animate function, I set it to false again (allowing for class changes as you scroll):
var s = $("#page-nav"),
pos = s.position(),
linkClicked = false;
$(window).scroll(function() {
var windowpos = $(window).scrollTop();
if (windowpos >= pos.top) {
s.addClass("stick");
$('.main').css('margin-top', '60px');
} else {
s.removeClass("stick");
$('.main').removeAttr('style');
}
});
$(window).scroll(function() {
var y = $(this).scrollTop();
$('.linky').each(function (event) {
id = $(this).attr('href');
if (y >= $(id).offset().top) {
if (!linkClicked) {
$('.linky').not(this).removeClass('active');
$(this).addClass('active');
}
}
});
});
//page nav
$("#page-nav li a").click(function(e) {
e.preventDefault();
$("#page-nav li a").removeClass('active');
$(this).addClass('active');
linkClicked = true;
goToByScroll($(this).attr("href").replace("#", ""));
});
function goToByScroll(id){
$('html,body').animate({
scrollTop: $("#"+id).offset().top},
'slow', function() {
linkClicked = false;
});
}
I have this script that makes a div become a link when resizing window. Problem is that when I zoom back out the div is still redirecting.
The code:
$(window).resize(function() {
if ($(this).width() <= 1400) {
$("#settings").click(function() {
window.location = "settings.php";
});
} else {
$("#settings").click(function() {
$("#settingsDropdown").toggle();
});
}
}).triggerHandler('resize');
I tried adding window.location = "#"; but it didn't work.
Both events are registered, you need to remove the first event handler for the click check the off event
you could do:
$(window).resize(function() {
if ($(this).width() <= 1400) {
$("#settings").off( 'click' ); // <------ Add this line
$("#settings").click(function() {
window.location = "settings.php";
});
} else {
$("#settings").off( 'click' ); // <------ Add this line
$("#settings").click(function() {
$("#settingsDropdown").toggle();
});
}
}).triggerHandler('resize');
You have to unbind event handlers which you don't want anymore
See unbind
use bind and unbind
$(window).resize(function() {
$("#settings").unbind('click.redirectOrToggle');
if ($(this).width() <= 1400) {
$("#settings").bind('click.redirectOrToggle', function() {
window.location = "settings.php";
});
} else {
$("#settings").bind('click.redirectOrToggle', function() {
$("#settingsDropdown").toggle();
});
}
}).triggerHandler('resize');
Instead of binding different handlers, change the logic within the click handler itself and you can remove the window.resize
/* called outside of window resize*/
$('#settings').click(function(){
if($(window).width() < 1400){
window.location = "settings.php";
}else{
$("#settingsDropdown").toggle();
}
})
Does jQuery Toggle break propagation?
My problem being is that I am building a Responsive Menu, and when I resize the browser to a width of the only breakpoint I have. jQuery's Toggle function breaks my propagation.
Here's the example: http://www.stlredtails.com/construction
If you resize your browser to less than or equal to 1024px, click on the Navigation button at the top, and try to click on the Contact link, once the menu is displayed. It doesn't propagate, and then if you resize the browser above 1024px... The Contact link does propagate. I do not recall having this problem with the Slide toggling.
Also if you resize the browser to or below 1024px. You have to refresh the page for the event to even fire once you click on the navigation button? Does jQuery's Toggle break this as well?
jQuery below:
if((window.outerWidth && window.outerWidth > 1024) || (document.body.clientWidth && document.body.clientWidth > 1024)){
jQuery(".navigation > ul > li").hoverIntent(
function() {
jQuery(this).children('ul').slideDown();
},
function(){
jQuery(this).children('ul').slideUp();
}
);
jQuery(".navigation > ul > li > ul").hover(
function() {
jQuery(this).children('ul').show();
},
function() {
jQuery(this).children('ul').hide();
}
);
}
if((window.outerWidth && window.outerWidth <= 1024) || (document.body.clientWidth && document.body.clientWidth <= 1024)){
jQuery(".navigation > ul > li > a").each( function() {
if(jQuery(this).siblings("ul").length) {
jQuery(this).removeAttr("href");
}
});
jQuery(".navigation > span > a#navigation").toggle(
function() {
jQuery(this).parent().siblings("ul").css("display", "block");
},
function() {
jQuery(this).parent().siblings("ul").css("display", "none");
}
);
jQuery(".navigation > ul > li > a").toggle(
function() {
jQuery(this).siblings("ul").css("display", "block");
},
function() {
jQuery(this).siblings("ul").css("display", "none");
}
);
}
Any suggestions?
Thank you!