Need some help. I am using jQuery in combination with media queries for some responsive aspects of my header. My client wants the header to be responsive to both devices and manual resize of the window. Therefore, I have set all of my responsive functions in both single functions and then set those same functions wrapped inside of a $(window).on('resize', function). Thanks to another poster on here, I was given the tip to wrap my responsive functions inside of a variable and then pass that variable to the resize function instead of rewriting it all out. This was a great tip and a great thing for me to learn as an intermediate javascript writer. However when I do this, the code does not work within the resize function. If I type it all out exactly the same, it works fine. But if I pass the function it does not work. Can someone please help? Code below:
var win = $(window);
var mainNav = $('.main-nav');
var navItem = $('.main-nav li');
var overlay = $('.nav-overlay');
var header = $('#header');
var subNav = $('.main-nav ul ul');
var overlayIsVisible = false;
var subNavIsVisible = false;
var exitMain = $('.exit-main');
var topNav = $('.top-nav');
var hamburger = $('.tablet-buttons .hamburger');
var exit = $('.tablet-buttons .exit');
if ( header.is('*') ) {
navItem.hover(function() {
overlay.fadeIn('slow');
$(this).find('ul').toggleClass("active");
overlayIsVisible = true;
});
exitMain.click(function() {
overlay.fadeOut('fast');
overlayIsVisible = false;
});
function tabletNav() {
if (win.width() <= 1024) {
topNav.prependTo(overlay);
hamburger.click(function() {
overlay.fadeIn('slow');
hamburger.css('display', 'none');
exit.fadeIn('slow');
});
exit.click(function(){
overlay.fadeOut('slow');
exit.css('display', 'none');
hamburger.fadeIn('slow');
});
}
else{
topNav.insertAfter('.logo');
}
};
/*This makes the above Mobile/Tablet functions also work on resize of window*/
win.on('resize', tabletNav());
};
Related
It's pretty hard to replicate because I use this code on a wordpress site and everything together gets to this result, but I try to explain it as good as possible.
I just want to know if I use it wrong or if its "illegal" to do it like I did so it would break something for sure, or if just wordpress or some other code on there messes this up.
First I got the following code:
$(document).ready(function () {
let li = $('.wpb_image_grid_ul li').not('#studio_gallery .wpb_image_grid_ul li');
let width = li.outerWidth();
li.css('height', width);
});
It resizes images inside my li to be square. It is working, but as soon as the browser gets resized and a breakpoint changes the width of the image, the height will stay the same as it got set before.
So I put my code in a function and called it once in the $(document).ready(function () and once in the $(window).resize(function() like this:
$(document).ready(function () {
function gallery_fix () {
let li = $('.wpb_image_grid_ul li').not('#studio_gallery .wpb_image_grid_ul li');
let width = li.outerWidth();
li.css('height', width);
}
gallery_fix();
});
$(window).resize(function() {
gallery_fix();
});
But that breaks the site, when I resize the window the page isn't responsive anymore and most of the content overflows.
When I change the code to this and just duplicate everything instead of calling the function twice it's working.
$(document).ready(function () {
function gallery_fix () {
let li = $('.wpb_image_grid_ul li').not('#studio_gallery .wpb_image_grid_ul li');
let width = li.outerWidth();
li.css('height', width);
}
gallery_fix();
});
$(window).resize(function() {
let li = $('.wpb_image_grid_ul li').not('#studio_gallery .wpb_image_grid_ul li');
let width = li.outerWidth();
li.css('height', width);
});
What am I doing wrong? Is my code not in the correct syntax? Or am I missing something here?
Your gallery_fix function is declared inside your $(document).ready() function. So, you can only call the function from within. You can either:
place your $(window).resize() handler within the $(document).ready()
declare the gallery_fix function outside
I have a working bottom function in JavaScript to detect if the user scrolls at the bottom. However, a problem comes when the user has a strange resolution (like windows scale) or when you zoom. The function is not working anymore and can't detect the bottom.
Here is what I did :
const bottom = e.target.scrollHeight - e.target.scrollTop === e.target.clientHeight;
if (bottom) {
this.props.getNewValues();
}
Is there a way to avoid that? Even when you don't zoom, this is not working for people displaying the site on a TV or something like this (like a friend of mine did)
Thanks you
EDIT : I'm applying this on a precise element and I repeat that my solution is working except by unzooming. Unzooming provides float values that made the response not really accurate (it goes from 1 to 50px of difference based on the zoom made)
I use this function (can't take credit as someone else wrote it - sorry for no credit - it was ages ago). Maybe you can adapt this to your use case:
(function($) {
//CHECK SCROLLED INTO VIEW UTIL
function Utils() {
}
Utils.prototype = {
constructor: Utils,
isElementInView: function (element, fullyInView) {
var pageTop = $(window).scrollTop();
var pageBottom = pageTop + $(window).height();
var elementTop = $(element).offset().top;
var elementBottom = elementTop + $(element).height();
if (fullyInView === true) {
return ((pageTop < elementTop) && (pageBottom > elementBottom));
} else {
return ((elementTop <= pageBottom) && (elementBottom >= pageTop));
}
}
};
var Utils = new Utils();
//END CHECK SCROLLED INTO VIEW UTIL
//USING THE ELEMENT IN VIEW UTIL
//this function tells what to do do when the element is or isnt in view.
//var inView = Utils.isElementInView(el, false); Where FALSE means the element doesnt need to be completely in view / TRUE would mean the element needs to be completely in view
function IsEInView(el) {
var inView = Utils.isElementInView(el, false);
if(inView) {
//console.log('in view');
} else {
//console.log('not in view');
}
};
//Check to make sure the element you want to be sure is visible is present on the page
var variableOfYourElement = $('#variableOfYourElement');
//if it is on this page run the function that checks to see if it is partially or fully in view
if( variableOfYourElement.length ) {
//run function on page load
IsEInView(variableOfYourElement);
//run function if the element scrolls into view
$(window).scroll(function(){
IsEInView(variableOfYourElement);
});
}
//END USING THE ELEMENT IN VIEW UTIL
})(jQuery);
I am trying to write a JQuery plugin called grid2carousel which takes some content in a Bootstrap-style grid on desktop devices and becomes a carousel on smaller screens.
The plugin works fine if it is the only instance of it on a page but runs into some problems if there are more than one. I have created a Codepen here to demonstrate the issue:
http://codepen.io/decodedcreative/pen/BzdBpb
Try commenting out one of the components in the HTML section of the codepen, resizing the browser til it becomes a carousel, and then repeating this process again with it uncommented
The plugin works by running an internal function called SetupPlugin every time the browser width is below a breakpoint specified in a data attribute in the HTML. If the browser width exceeds this breakpoint a function called DestroyPlugin reverts the HTML back to its original state. Like so:
checkDeviceState = function(){
if($(window).width()>breakpointValue){
destroyPlugin();
}else{
if(!$element.hasClass('loaded')){
setupPlugin();
}
}
},
Below is my plugin code in its entirety. Could someone give me a pointer as to what I'm doing wrong here?
(function (window, $){
$.grid2Carousel = function (node, options){
var
options = $.extend({slidesSelector: '.g2c-slides', buttonsSelector: '.g2c-controls .arrow'}, {},options),
$element = $(node),
elementHeight = 0,
$slides = $element.find(options.slidesSelector).children(),
$buttons = $element.find(options.buttonsSelector),
noOfItems = $element.children().length + 1,
breakpoint = $element.data("bp"),
breakpointValue = 0;
switch(breakpoint){
case "sm":
breakpointValue = 767;
break;
case "md":
breakpointValue = 991;
break;
case "lg":
breakpointValue = 1199;
break;
}
setupPlugin = function(){
// Add loaded CSS class to parent element which adds styles to turn grid layout into carousel layout
$element.addClass("loaded");
// Get the height of the tallest child element
elementHeight = getTallestInCollection($slides)
// As the carousel slides are stacked on top of each other with absolute positioning, the carousel doesn't have a height. Set its height using JS to the height of the tallest item;
$element.height(elementHeight);
// Add active class to the first slide
$slides.first().addClass('active');
$buttons.on("click", changeSlide);
},
destroyPlugin = function(){
$element.removeClass("loaded");
$element.height("auto");
$buttons.off("click");
$slides.removeClass("active");
},
checkDeviceState = function(){
if($(window).width()>breakpointValue){
destroyPlugin();
}else{
if(!$element.hasClass('loaded')){
setupPlugin();
}
}
},
changeSlide = function(){
var $activeSlide = $slides.filter(".active"),
$nextActive = null,
prevSlideNo = $activeSlide.prev().index() + 1,
nextSlideNo = $activeSlide.next().index() + 1;
if($(this).hasClass('left')){
if(prevSlideNo !== 0){
$nextActive = $activeSlide.prev();
$nextActive.addClass('active');
$slides.filter(".active").not($nextActive).removeClass("active");
}else{
$nextActive = $slides.last();
$nextActive.addClass('active');
$slides.filter(".active").not($nextActive).removeClass("active");
}
}else if($(this).hasClass('right')){
if(nextSlideNo !== 0){
$nextActive = $activeSlide.next();
$nextActive.addClass('active');
$slides.filter(".active").not($nextActive).removeClass("active");
}else{
$nextActive = $slides.first();
$nextActive.addClass('active');
$slides.filter(".active").not($nextActive).removeClass("active");
}
}
},
getTallestInCollection = function(collection){
$(collection).each(function(){
if($(this).outerHeight() > elementHeight){
elementHeight = $(this).outerHeight();
}
});
return elementHeight;
};
setupPlugin();
checkDeviceState();
$(window).on("resize", checkDeviceState);
}
$.fn.grid2Carousel = function (options) {
this.each( function (index, node) {
$.grid2Carousel(node, options)
});
return this
}
})(window, jQuery);
Many thanks,
James
Please check line #30 in your plugin code,it looks that you've just forget to add var keyword so instead of creating local variables to store functions setupPlugin, destoryPlugin and so on you've created global variables and then each new initialization of your plugin is rewriting this functions to point to a newly created slider.
setupPlugin = function(){
should be
var setupPlugin = function(){
Updated code here.
Just wondering if anyone has an idea as to how I might re-create a nav bar style that I saw a while ago, I just found the site I saw it on, but am not sure how they might have gotten there. Basically want it to scroll with the page then lock to the top...
http://lesscss.org/
Just do a quick "view source" on http://lesscss.org/ and you'll see this:
window.onscroll = function () {
if (!docked && (menu.offsetTop - scrollTop() < 0)) {
menu.style.top = 0;
menu.style.position = 'fixed';
menu.className = 'docked';
docked = true;
} else if (docked && scrollTop() <= init) {
menu.style.position = 'absolute';
menu.style.top = init + 'px';
menu.className = menu.className.replace('docked', '');
docked = false;
}
};
They're binding to the onscroll event for the window, this event is triggered when the window scrolls. The docked flag is set to true when the menu is "locked" to the top of the page, the menu is set to position:fixed at the same time that that flag is set to true. The rest is just some simple "are we about to scroll the menu off the page" and "are we about back where we started" position checking logic.
You have to be careful with onscroll events though, they can fire a lot in rapid succession so your handler needs to be pretty quick and should precompute as much as possible.
In jQuery, it would look pretty much the same:
$(window).scroll(function() {
// Pretty much the same as what's on lesscss.org
});
You see this sort of thing quite often with the "floating almost fixed position vertical toolbar" things such as those on cracked.com.
mu is too short answer is working, I'm just posting this to give you the jquery script!
var docked = false;
var menu = $('#menu');
var init = menu.offset().top;
$(window).scroll(function()
{
if (!docked && (menu.offset().top - $("body").scrollTop() < 0))
{
menu.css({
position : "fixed",
top: 0,
});
docked = true;
}
else if(docked && $("body").scrollTop() <= init)
{
menu.css({
position : "absolute",
top: init + 'px',
});
docked = false;
}
});
Mu's answer got me far. I tried my luck with replicationg lesscss.org's approach but ran into issues on browser resizing and zooming. Took me a while to find out how to react to that properly and how to reset the initial position (init) without jQuery or any other library.
Find a preview on JSFiddle: http://jsfiddle.net/ctietze/zeasg/
So here's the plain JavaScript code in detail, just in case JSFiddle refuses to work.
Reusable scroll-then-snap menu class
Here's a reusable version. I put the scrolling checks into a class because the helper methods involved cluttered my main namespace:
var windowScrollTop = function () {
return window.pageYOffset;
};
var Menu = (function (scrollOffset) {
var Menu = function () {
this.element = document.getElementById('nav');
this.docked = false;
this.initialOffsetTop = 0;
this.resetInitialOffsetTop();
}
Menu.prototype = {
offsetTop: function () {
return this.element.offsetTop;
},
resetInitialOffsetTop: function () {
this.initialOffsetTop = this.offsetTop();
},
dock: function () {
this.element.className = 'docked';
this.docked = true;
},
undock: function () {
this.element.className = this.element.className.replace('docked', '');
this.docked = false;
},
toggleDock: function () {
if (this.docked === false && (this.offsetTop() - scrollOffset() < 0)) {
this.dock();
} else if (this.docked === true && (scrollOffset() <= this.initialOffsetTop)) {
this.undock();
}
}
};
return Menu;
})(windowScrollTop);
var menu = new Menu();
window.onscroll = function () {
menu.toggleDock();
};
Handle zoom/page resize events
var updateMenuTop = function () {
// Shortly dock to reset the initial Y-offset
menu.undock();
menu.resetInitialOffsetTop();
// If appropriate, undock again based on the new value
menu.toggleDock();
};
var zoomListeners = [updateMenuTop];
(function(){
var w = window,
d = document,
e = d.documentElement,
g = d.getElementsByTagName('body')[0];
var lastWidth = 0;
function pollZoomFireEvent() {
var widthNow = w.innerWidth || e.clientWidth || g.clientWidth;
if (lastWidth == widthNow) {
return;
}
lastWidth = widthNow;
// Length changed, user must have zoomed, invoke listeners.
for (i = zoomListeners.length - 1; i >= 0; --i) {
zoomListeners[i]();
}
}
setInterval(pollZoomFireEvent, 100);
})();
Sounds like an application of Jquery ScrollTop and some manipulation of CSS properties of the navbar element. So for example, under certain scroll conditions the navbar element is changed from absolute positioning with calculated co-ordinates to fixed positioning.
http://api.jquery.com/scrollTop/
The effect you describe would usually start with some type of animation, like in TheDeveloper's answer. Default animations typically slide an element around by changing its position over time or fade an element in/out by changing its opacity, etc.
Getting the "bouce back" or "snap to" effect usually involves easing. All major frameworks have some form of easing available. It's all about personal preference; you can't really go wrong with any of them.
jQuery has easing plugins that you could use with the .animate() function, or you can use jQueryUI.
MooTools has easing built in to the FX class of the core library.
Yahoo's YUI also has easing built in.
If you can remember what site it was, you could always visit it again and take a look at their source to see what framework and effect was used.
Slashdot has a little widget that allows you to tweak your comment threshold to filter out down-modded comments. It will be in one place if you scroll to the top of the page, and as you scroll down, at some point, where its original home is about to scroll off the page, it will switch to fixed position, and stay on your screen. (To see an example, click here.)
My question is, how can I accomplish the same effect of having a menu be in one place when scrolled up, and switch to fixed position as the user scrolls down? I know this will involve a combination of CSS and javascript. I'm not necessarily looking for a full example of working code, but what steps will my code need to go through?
Okay, I figured it out. I will post it here in case it help anyone else. This solution uses prototype, and an internal library that gives me the registerEvent, getElementX and getElementY functions, which do what you would think.
var MenuManager = Class.create({
initialize: function initialize(menuElt) {
this.menu = $(menuElt);
this.homePosn = { x: getElementX(this.menu), y: getElementY(this.menu) };
registerEvent(document, 'scroll', this.handleScroll.bind(this));
this.handleScroll();
},
handleScroll: function handleScroll() {
this.scrollOffset = document.viewport.getScrollOffsets().top;
if (this.scrollOffset > this.homePosn.y) {
this.menu.style.position = 'fixed';
this.menu.style.top = 0;
this.menu.style.left = this.homePosn.x;
} else {
this.menu.style.position = 'absolute';
this.menu.style.top = null;
this.menu.style.left = null;
}
}
});
Just call the constructor with the id of your menu, and the class will take it from there.
Thanks for the effort of sharing this code.
I made some small changes to make it work with the current release of Prototype.
var TableHeaderManager = Class.create({
initialize: function initialize(headerElt) {
this.tableHeader = $(headerElt);
this.homePosn = { x: this.tableHeader.cumulativeOffset()[0], y: this.tableHeader.cumulativeOffset()[1] };
Event.observe(window, 'scroll', this.handleScroll.bind(this));
this.handleScroll();
},
handleScroll: function handleScroll() {
this.scrollOffset = document.viewport.getScrollOffsets().top;
if (this.scrollOffset > this.homePosn.y) {
this.tableHeader.style.position = 'fixed';
this.tableHeader.style.top = 0;
this.tableHeader.style.left = this.homePosn.x;
} else {
this.tableHeader.style.position = 'absolute';
this.tableHeader.style.top = null;
this.tableHeader.style.left = null;
}
}
});
For a demo but not based on the code above checkout:
fixed-floating-elements