slideToggle() Function does not work after scaling down - javascript

I got a confusing bug inside my code and do not know how to fix it. I got to main functions, calling scripts depending on the current window size.
For mobile devices I got a slideToggle() function, which does work when viewing on a mobile device. But when opening above 600px, and scaling down the function is fired, but the slideToggle(); does not work anymore.
Its set to display: block; and immediately set to display: none; again. My slideToggle(); is embedded like this:
$('.box-header').on('click', function() {
$(this).parent().toggleClass('folded').toggleClass('unfolded');
$(this).next('div').stop().slideToggle();
console.log('clicked');
});
JSFIDDLE DEMO
When viewing the demo its important to load the page while the window size is above 600px and than scale down to recreate the problem. If it works please try again, as it does work sometimes.
What am I missing? And is there a better approach to kill and call scripts depending on the viewers viewport after resize events?

Put the if in the click event:
$('.box-header').on('click', function() {
if ($(window).width() < 600) {
$(this).parent().toggleClass('folded unfolded');
$(this).next('div').stop().slideToggle();
} else {
//for other code here for medium+large screens
}
});
https://jsfiddle.net/5puqn9ts/1/
even use a class to toggle between the mobile and large screen, bind a click event to each
$(document).ready(function() {
$('body').on('click', '.mobile', function() {
$(this).parent().toggleClass('folded').toggleClass('unfolded');
$(this).next('div').stop().slideToggle();
console.log('clicked');
});
// init scripts for smartphone or desktops
var initScripts = function() {
if ($(window).width() < 600) {
$('.box-header').addClass('mobile');
}
if ($(window).width() >= 600) {
$('.box-header').removeClass('mobile');
/add class for medium screen then bind event to ti
}
}
initScripts();
var id;
$(window).resize(function() {
initScripts()
});
});
https://jsfiddle.net/75186j96/5/

Your function set an event in every call. Try this correct alternative:
var smartphoneFunctions = $('.box-header').on('click', function() {
$(this).parent().toggleClass('folded').toggleClass('unfolded');
$(this).next('div').stop().slideToggle();
console.log('clicked');
});
https://jsfiddle.net/75186j96/8/

To answer your question, you need to adjust your slideToggle() target to be .box-content rather than the abstract div as this will instead apply the toggle on the .box-header. Also your class toggling could be bundled.
The resulting code would be the following:
$('.box-header').on('click', function() {
$(this).parent().toggleClass('folded unfolded');
$(this).next('.box-content').stop().slideToggle();
console.log('clicked');
});
That being said, there are better ways of doing this.
You could tie your classes to the appearance of .box-content, like this:
.box-content {
transition: height 300ms;
overflow: hidden;
}
.box.folded .box-content {
max-height: 0;
}
.box.unfolded .box-content {
max-height: 300px;
}

Related

JavaScript window resize event is slow. How to optimize window resize event

When I click on the Maximize button of the browser window, a function I wrote that will execute when the window is resized, does not work properly I think because JavaScript window resize event is running slow. It worked when I used the mouse to resize the window. Also, when I tried to change between portrait mobile to landscape mobile it is also slow.
Faced a problem with using with window.addEventListener('resize', aFunction), page gets slow. These events are generated multiple times per second, and if the event handler takes too much time, the browser won’t catch with redrawing the page.
function aFuntion() {
let div1 = document.querySelector('.div1');
let div2 = document.querySelector('.div2');
let diff = div1.clientHeight - div2.clientHeight;
div2.style.top = diff + 'px';
};
// Call the function.
// This worked!
aFuntion();
// Call the function when users resize the window.
// This worked!
window.addEventListener('resize', aFuntion);
// Users click on the Maximize button of the window.
// The function does not work properly!
// Portrait mobile to Landscape mobile.
// The function does not work properly!
.div1 {
position: relative;
}
.div2 {
position: absolute;
width: 300px;
top: 0;
transition: all 0.5s ease;
}
#media only screen and (max-width: 1024px) {
.div2 {
width: 200px;
}
}
<div class="div1">
<div class="div2"></div>
</div>
I removed transition: all 0.5s ease; and it worked for a while now it is not working again.
So, I know now it is because of window.addEventListener('resize', function) is running slow.
Try this:
var timeout = false;
window.addEventListener('resize', function() {
clearTimeout(timeout);
timeout = setTimeout(aFuntion, 200);
});
Read: https://bencentra.com/code/2015/02/27/optimizing-window-resize.html
try with document.addEventListener('resize', aFuntion);

jquery document scroll not firing

I have a fixed navbar on my site that I'm trying to tie functions to once it reaches a certain point on the page. I've done this successfully three times before on three sites but can't for the life of me get it to work on this one. The function is wrapped in a window ready so I know the page is fully loaded -- completely stumped for two days... Here the code:
jQuery:
function startchange() {
$('#ajax-frame').imagesLoaded().done(function(instance) {
var scroll_start = 0;
var startchange = $('.startchange');
var offset = startchange.offset();
if (startchange.length) {
$(document).on( 'scroll', function() {
scroll_start = $(this).scrollTop();
if (scroll_start > offset.top) {
$('nav').addClass('active');
console.log("startchange working");
} else {
$('nav').removeClass('active');
};
});
}
});
};
CSS:
body,
html {
height: 100% !important;
width: 100%;
margin: 0;
padding: 0;
overflow-x: hidden;
}
Thanks for any insight into this frustrating issue.
One quick approach that will sove the issue is to remove: overflow-x: hidden; from your css. Demo: https://jsfiddle.net/mrlew/ce8me3qk/
But here is what's happening: you're setting body and html to height 100%, and one is overlapping the other (html tag is a block element too). You're setting both to height: 100% and actually what you're scrolling is body, and not window/document.
Proof: look at both scrollbar there when setting overflow to scroll: https://jsfiddle.net/mrlew/ce8me3qk/8/ Note that you're scrolling the inner one. So, if you change $("document").on('scroll', function() { to $("body").on('scroll', function() {, it will work too.
Or, just don't set html height to 100%.

using jQuery slide toggle with min height

I am trying to use jQuerys slideToggle on a div that has a minimum height. this causes the animation to jump and not move smoothly. I have spent a while now looking for a solution but i am unable to get anything to work. I have made an example on jsfiddle and would appreciate if anyone could help me solve this issue.
my css for the div being toggled:
#selected-display{
height:calc(100vh - 50px);
display:none;
min-height: 750px;
}
my javascript:
$(document).ready(function(){
$(".toggle-display").click(function(){
$("#selected-display").slideToggle("slow");
});
});
https://jsfiddle.net/4y3q27mh/
https://jsfiddle.net/rqkt58L1/
You could just disable min-height during the animation, and then turn it back on when the animation is over:
$(document).ready(function () {
$(".toggle-display").click(function () {
var minHeight = $("#selected-display").css('min-height');
$("#selected-display").css('min-height',0).slideToggle("slow", function() {
$(this).css('min-height', minHeight);
});
});
});
Answer by dave works, but there is no animation to min-height. it just "appears"
I have solve it with javascript without using min-height:
function toggle(item) {
if (item.height() < 350) {
item.css('height', 350);
}
item.slideToggle();
}

Manipulating the DOM using jQuery .resize()

I want to change the order of elements in the DOM based on different browser sizes.
I've looked into using intention.js but feel that it might be overkill for what I need (it depends on underscore.js).
So, i'm considering using jQuery's .resize(), but want to know if you think something like the following would be acceptable, and in line with best practices...
var layout = 'desktop';
$( window ).resize(function() {
var ww = $( window ).width();
if(ww<=767 && layout !== 'mobile'){
layout = 'mobile';
// Do something here
}else if((ww>767 && ww<=1023) && layout !== 'tablet'){
layout = 'tablet';
// Do something here
}else if(ww>1023 && layout !== 'desktop'){
layout = 'desktop';
// Do something here
}
}).trigger('resize');
I'm storing the current layout in the layout variable so as to only trigger the functions when the window enters the next breakpoint.
Media queries are generally preferred. However, if I am in a situation where I am in a single page application that has a lot of manipulation during runtime, I will use onresize() instead. Javascript gives you a bit more freedom to work with dynamically setting breakpoints (especially if you are moving elements around inside the DOM tree with stuff like append()). The setup you have is pretty close to the one I use:
function setWidthBreakpoints(windowWidth) {
if (windowWidth >= 1200) {
newWinWidth = 'lg';
} else if (windowWidth >= 992) {
newWinWidth = 'md';
} else if (windowWidth >= 768) {
newWinWidth = 'sm';
} else {
newWinWidth = 'xs';
}
}
window.onresize = function () {
setWidthBreakpoints($(this).width());
if (newWinWidth !== winWidth) {
onSizeChange();
winWidth = newWinWidth;
}
};
function onSizeChange() {
// do some size changing events here.
}
The one thing that you have not included that is considered best practice is a debouncing function, such as the one below provided by Paul Irish, which prevents repeated firing of the resize event in a browser window:
(function($,sr){
// debouncing function from John Hann
// http://unscriptable.com/index.php/2009/03/20/debouncing-javascript-methods/
var debounce = function (func, threshold, execAsap) {
var timeout;
return function debounced () {
var obj = this, args = arguments;
function delayed () {
if (!execAsap)
func.apply(obj, args);
timeout = null;
};
if (timeout)
clearTimeout(timeout);
else if (execAsap)
func.apply(obj, args);
timeout = setTimeout(delayed, threshold || 100);
};
}
// smartresize
jQuery.fn[sr] = function(fn){ return fn ? this.bind('resize', debounce(fn)) : this.trigger(sr); };
})(jQuery,'smartresize');
// usage:
$(window).smartresize(function(){
// code that takes it easy...
});
So incorporate a debouncer into your resize function and you should be golden.
In the practice is better to use Media Queries
Try this, I'm in a hurry atm and will refactor later.
SCSS:
body, html, .wrapper { width: 100%; height: 100% }
.sidebar { width: 20%; height: 500px; float: left;
&.mobile { display: none } }
.content { float: right; width: 80% }
.red { background-color: red }
.blue { background-color: blue }
.green { background-color: green }
#media all and (max-width: 700px) {
.content { width: 100%; float: left }
.sidebar { display: none
&.mobile { display: block; width: 100% }
}
}
HAML
.wrapper
.sidebar.blue
.content.red
.content.green
.sidebar.mobile.blue
On 700 px page breaks, sidebar disappears and mobile sidebar appears.
This can be much more elegant but you get the picture.
Only possible downside to this approach is duplication of sidebar.
That's it, no JS.
Ok, the reason for my original question was because I couldn't find a way to move a left sidebar (which appears first in the HTML) to appear after the content on mobiles.
Despite the comments, I still can't see how using media queries and position or display alone would reliably solve the problem (perhaps someone can give an example?).
But, it did lead me to investigate the flexbox model - display: flex, and so I have ended up using that, and specifically flex's order property to re-arrange the order of the sidebars and content area.
Good guide here - https://css-tricks.com/snippets/css/a-guide-to-flexbox/

I can add an overlay, but I can't remove it (jQuery)

This function adds an overlay with the following properties to the entire browser screen,
$('a.cell').click(function() {
$('<div id = "overlay" />').appendTo('body').fadeIn("slow");
});
#overlay
{
background-color: black;
width: 100%;
height: 100%;
position: fixed;
top: 0;
left: 0;
display: none;
z-index: 100;
opacity: 0.5;
}
And this function is supposed to remove it.
$('#overlay').click(function() {
$(this).fadeOut("slow").remove();
});
But it seems to do absolutely nothing and now my page is stuck with a black overly over it. What's wrong with the removal?
The problem is that when you're adding the click handler, there isn't any overlay, so you're adding the handler to an empty set of elements.
To fix this, use the live method to bind your handler to all elements that match #overlay, whenever they are created.
Also, fadeOut is not a blocking call, so it returns before the element finishes fading out. Therefore, you're calling remove right after the element starts fading out.
To fix this, use fadeOut's callback parameter to call remove after the animation finishes.
For example:
$('#overlay').live(function() {
$(this).fadeOut("slow", function() { $(this).remove(); });
});
Here you go. This should fix the problem and let the overlay fade out before removing it.
$('#overlay').live("click", function() {
$(this).fadeOut("slow", function() { $(this).remove() });
});
Remove should be in the callback to fadeout, like so:
$('#overlay').live('click', function() {
$(this).fadeOut("slow", function() {
$(this).remove();
});
});
Try:
$('#overlay').live('click', function() {
$(this).fadeOut("slow").remove();
});
My recommendation is to use the jquery.tools overlay plugin. Your overlay will have a trigger (usually a button or link), but you can load or clear it with a javascript command, e.g.:
js:
var config = { closeOnClick:true, mask:{opacity:0.7, color:'#333', loadSpeed:1} }
$("#myTrigger").overlay(config); // add overlay functionality
$("#myTrigger").data("overlay").load(); // make overlay appear
$("#myTrigger").data("overlay").close(); // make overlay disappear
html:
<div id="myOverlay" style="display:none;">Be sure to set width and height css.</div>
<button id="myTrigger" rel="#myOverlay">show overlay</button>

Categories