Third day I'm trying to implement media queries in JavaScript.
Say function A() can be called only if (min-width: 768px),
and the function B() can be called only if (max-width: 767px).
This is easily achieved by using MediaQueryList object. But problems occur with browser resizing.
A function A() can not be called if the page was loaded on
(max-width: 767px), and then resizing to (min-width: 768px).
A function A() fires multiple times on click if I try call function on window resize.
I have tried different solutions:
using addListener
enquire.js
setTimeout / clearTimeout — http://go.shr.lc/1kGNpM6
etc
But obviously my knowledge of JavaScript is not enough to do things write. Please help
// Attempt #1 -----------------------------------------------------------------
function responsiveFunction(){
if(window.matchMedia('(max-width: 767px)').matches) {
$('.btn').click(function(event) {
// Knock knock
});
}
}
$(function(){
responsiveFunction();
});
$(window).resize(function(){
responsiveFunction();
});
// Attempt #2 -----------------------------------------------------------------
function responsiveFunction(mql) {
if (mql.matches) {
$('.btn').click(function(event) {
// Knock knock
});
}
}
var mql = window.matchMedia('min-width: 768px'); // MQL for MediaQueryList object
mql.addListener(responsiveFunction); // Execute responsive function on resize
responsiveFunction(mql); // Execute responsive function on load
// Attempt #3 -----------------------------------------------------------------
var smartResize = (function() {
var timers = {};
return function(callback, ms, uniqueId) {
if (!uniqueId) {
uniqueId = 'Don\'t call this twice without a uniqueId';
}
if (timers[uniqueId]) {
clearTimeout(timers[uniqueId]);
}
timers[uniqueId] = setTimeout(callback, ms);
};
})();
function responsiveFunction() {
if (window.matchMedia('(min-width: 768px)').matches) {
$('.btn').click(function(event) {
// Knock knock
});
}
}
// Execute responsive function on load
responsiveFunction();
// Execute responsive function on resize
$(window).resize(function() {
smartResize(function() {
responsiveFunction();
}, 500, 'myUniqueId');
});
// Attempt #4 w enquire.min.js ---------------------------------------------
enquire.register('(min-width: 768px)', {
match: function() {
$('.btn').click(function(event) {
// Knock knock
});
}
});
This should work:
$(function(){
$('.btn').on('click', function(event) {
if(window.matchMedia('(max-width: 767px)').matches) {
// Only run the code if media query matches
}
});
});
Register the click handler without checking the max-width and check the width just before you run the code, if the width condition matches then run the code otherwise doesn't run the code.
Related
I'm trying to launch different functions depending on screen size after load, resize and scroll. To prevent effect of calculations in other functions I need to stop them.
Here if example with dummy functions from Jsfiddle
Here is example with real functions from Jsfiddle (it doesn't show html or css, just functions for your understanding)
If you look at console you will see that after first rezise first function runs, but when you get another window size breakpoint another function will run together with first one. the same with third function if you get third breakpoint. I want to stop function after window changed size to another breakpoint
here is my jquery:
function sticker1220() {
$(window).on("load resize scroll",function(){
console.log('sticker1220');
});
};
function sticker950() {
$(window).on("load resize scroll",function(){
console.log('sticker950');
});
};
function sticker320() {
$(window).on("load resize scroll",function(){
console.log('sticker320');
});
};
function checksize() {
if ( $(window).width() > 1220 ) {
sticker1220();
} else if ( $(window).width() > 640 & $(window).width() < 1219 ) {
sticker950();
} else if ( $(window).width() < 639 ) {
sticker320();
}
};
checksize();
$(window).resize(function() {
checksize();
$('p').text($(window).width());
});
You could do something like this.
I have abstracted the media queries into an object that can be iterated to find the applicable callback if one is available.
There is also now only on resize callback which is easier to manage as you don't need to run separate resize events which could lock up your ui thread. And the events will work properly if you change the screen resolution, whereas previously were binding a new resize event each time the screen resized.
If you need to make it faster you could change the resize event to a recursive timer using setTimeout.
function checksize(mediaQueries) {
return function(e) {
const width = window.innerWidth
$('p').text(width)
const query = mediaQueries.find(x => {
switch (true) {
case !!x.min && !!x.max:
return width >= x.min && width < x.max
case !!x.min:
return width >= x.min
case !!x.max:
return width <= x.max
default:
return false
}
})
if (typeof query.cb === 'function') {
return query.cb(e)
}
}
}
var atMedia = [
{
max: 639,
cb: function(e) {
console.log('sticker320')
}
},
{
min: 640,
max: 1219,
cb: function(e) {
console.log('sticker950')
}
},
{
min: 1220,
cb: function(e) {
console.log('sticker1220')
}
}
]
$(window).resize(checksize(atMedia)).trigger('resize')
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<p></p>
Hi i have some problem to ask you. Now i need to print page after function columnize is finished but print function is work before columnize function is finishing. How to print page after columnize function finished in script .
<script>
$(function () {
var content_height = 711; // the height of the content, discluding the header/footer
var page = 1; // the beginning page number to show in the footer
function buildNewsletter() {
if ($('#newsletterContent').contents().length > 0) {
// when we need to add a new page, use a jq object for a template
// or use a long HTML string, whatever your preference
$page = $("#page_template").clone().addClass("page").css("display", "block");
// fun stuff, like adding page numbers to the footer
$page.find(".footer #lbl_pageNum").append(page);
$page.find(".footer #lbl_pageNum2").append(page);
//create label to recive data
$("body").append($page);
page++;
// here is the columnizer magic
$('#newsletterContent').columnize({
columns: 1,
target: ".page:last .content",
overflow: {
height: content_height,
id: "#newsletterContent",
doneFunc: function () {
console.log("done with page");
buildNewsletter();
}
}
});
}
}
setTimeout(buildNewsletter, 1000);
});
function printForm() {
window.print();
}
doneFunc: function () {
console.log("done with page");
printForm();
Is this what you intended?
From what I understand, this runs when the function is finished. Therefore, have the printForm() function run during the doneFunc() function.
I am extremely new to JS, any help with this question is greatly appreciated.
I have the following bit of JS:
var isMobile = false;
if(Modernizr.mq('only all and (max-width: 1024px)') ) {
isMobile = true;
}
if (isMobile === false)
/* desired function: */
slider.ev.on('rsAfterSlideChange', playSlideVideo);
playSlideVideo();
}
Which indeed works as intended if the browser window loads below or above max-width: 1024px. The problem is that I would like my functions to run or stop if the user changes the size of their window after the page loads.
How can I go about setting that up?
This is amazing, thank you! Specially for explaining commenting your code along the way. Im 80% there. the script works when going from small window to big, but does not work from big to small. any thoughts on how I can fix? This is all of my code:
var slider = $(".royalSlider").data('royalSlider'),
prevSlideVideo,
playSlideVideo = function() {
if(prevSlideVideo) {
prevSlideVideo.pause();
}
var video = slider.currSlide.content.find('video');
if(video.length) {
prevSlideVideo = video[0];
prevSlideVideo.play();
} else {
prevSlideVideo = null;
}
};
var $window = $(window); // have a reference to window
var isMobile = false; // boolean to check for mobile
var timer = 0;
function updateSlider() {
if (!isMobile) {
/* desired shenanigans: */
slider.ev.on('rsAfterSlideChange', playSlideVideo);
playSlideVideo();
}
}
$window.on("resize.slider", function() {
// delay execution to 300ms after the last resize event
timer = setTimeout(function() {
clearTimeout(timer);
isMobile = Modernizr.mq('only all and (max-width: 1024px)'); // update isMobile with mq value
updateSlider(); // run function on resize
}, 300);
}).trigger("resize.slider"); // trigger this event so you dont have to explictly call updateSlider();
update
I would doubt the isMobile = Modernizr.mq('only all and (max-width: 1024px)'); part (even though it seems fine)
Also, it appears that you're assigning events to slider widget. There could be a case where multiple resize events would end up attaching multiple events 'rsAfterSlideChange' to the slider.
Looking at the api there doesn't seem to be an off method.
So, keeping above in mind. could you try something like below:
var $window = $(window); // have a reference to window
var isMobile = false; // boolean to check for mobile
var timer = 0;
var slider = $(".royalSlider").data('royalSlider'); // slider instance
var prevSlideVideo;
var playSlideVideo = function() {
if(prevSlideVideo) {
prevSlideVideo.pause();
}
var video = slider.currSlide.content.find('video');
if(video.length) {
prevSlideVideo = video[0];
prevSlideVideo.play();
} else {
prevSlideVideo = null;
}
};
// Looking at the [api](http://dimsemenov.com/plugins/royal-slider/documentation/#api) there doesn't seem to be an ```off``` method.
// So it is probably safe to attach event only once.
slider.ev.on('rsAfterSlideChange', playSlideVideo);
$window.on("resize.slider", function() {
// delay execution to 300ms after the last resize event
timer = setTimeout(function() {
clearTimeout(timer);
// quick and dirty check of window width instead of match media, this is unreliable but should do the job for now
if ($window.width() < 1024) {
playSlideVideo();
}
// updateSlider(); // dont need this anymore as you're calling ```playSlideVideo()``` straight away
}, 300);
}).trigger("resize.slider"); // trigger this event so you dont have to explictly call updateSlider();
There are many ways to do it.
Heres a quick way:
var $window = $(window); // have a reference to window
var isMobile = false; // boolean to check for mobile
function updateSlider() {
if (!isMobile) {
/* desired shenanigans: */
slider.ev.on('rsAfterSlideChange', playSlideVideo);
playSlideVideo();
}
}
$window.on("resize.slider", function() {
isMobile = Modernizr.mq('only all and (max-width: 1024px)'); // update isMobile with mq value
updateSlider(); // run function on resize
}).trigger("resize.slider"); // trigger this event so you don't have to explicitly call updateSlider();
Interesting thing with resize events is that they will fire many times when window is resized, there are many ways to throttle this event, below is one way
var $window = $(window); // have a reference to window
var isMobile = false; // boolean to check for mobile
var timer = 0;
function updateSlider() {
if (!isMobile) {
/* desired shenanigans: */
slider.ev.on('rsAfterSlideChange', playSlideVideo);
playSlideVideo();
}
}
$window.on("resize.slider", function() {
// delay execution to 300ms after the last resize event
timer = setTimeout(function() {
clearTimeout(timer);
isMobile = Modernizr.mq('only all and (max-width: 1024px)'); // update isMobile with mq value
updateSlider(); // run funtion on resize
}, 300);
}).trigger("resize.slider"); // trigger this event so you dont have to explictly call updateSlider();
I haven't tested above code (maybe provide a fiddle?) but should put you in right direction. Let me know if anything is unclear.
Good luck
I'm using the below jquery to check and reset the height of my div on resize. However, there is a terrible freeze/lag when using the below code. The structure is just three absolutely positioned divs. One at the top, one at the bottom, and one in the middle. Any idea why this might be happening?
$(window).resize(function () {
if ($("#master_bodywrapper_div").height() < 500) {
$("#master_bodywrapper_div").height(500);
}
else {
$("#master_bodywrapper_div").height("auto");
}
});
This is because, on some browsers, the resize event can fire dozens of times per second. You should have a resizing function that is called only after the window is done being resized, like so:
(function($) {
var resizeTimer = false;
function doResize() {
if ($("#master_bodywrapper_div").height() < 500) {
$("#master_bodywrapper_div").height(500);
}
else {
$("#master_bodywrapper_div").height("auto");
}
resizeTimer = false;
}
$(window).on("resize", function() {
if (resizeTimer) {
clearTimeout(resizeTimer);
}
resizeTimer = setTimeout(doResize, 300);
});
})(jQuery);
Hope this helps!
Well, it is querying the DOM twice every time resize is triggered and this function called, which might be the source (DOM querying can be expensive). Note that resize is triggered a lot even if you simply grab the edge and move your mouse (with a moderate speed) 200px to one side. Try moving the $("#master_bodywrapper_div") query to above the resize callback, cached in a var, and then use that var reference in your callback function.
var div = $("#master_bodywrapper_div");
$(window).resize(function () {
if (div.height() < 500) {
div.height(500);
}
else {
div.height("auto");
}
});
I am creating a mobile first responsive web site that has a few custom jquery plugins which kick in at certain breakpoints.
First, snippets of the code, and then ill explain the issue...
CSS:
#media (min-width: 20em) {
html:after {
content: "320";
} }
#media (min-width: 25em) {
html:after {
content: "400";
} }
#media (min-width: 30em) {
html:after {
content: "480";
} }
GLOBAL JS
var FOO = {
mq: function() {
return parseInt(window.getComputedStyle(document.documentElement,':after').getPropertyValue('content').replace(/['"]/g,''),10);
}
}
JQUERY PLUGIN 1
this.init = function()
{
$(window).on("load resize orientationchange", function() {
if(FOO.mq() >= 480) {
DO SOMETHING
}
});
};
JQUERY PLUGIN 2
this.init = function()
{
$(window).on("load resize orientationchange", function() {
if(FOO.mq() >= 560) {
DO SOMETHING ELSE
}
});
};
Now the problem with putting the FOO.mq() in the jquery plugin is that it will fire the FOO.mq() function for every DOM object found (e.g. if the plugin does something to 10 images it will fire the FF.mq() 10 times) where really thats not needed as we only need to check once.
I have tried putting the resize/return value in a global object but it nevers seems to work and gets picked up bu the jquery plugins.
Any pointers would be greatly appreciated.
Thanks
Assuming that this calculated value only changes on document load, or if the window changes layout:
var FOO = FOO || {}; // create global namespace obj, if required
$(function() {
var mq; // scoped variable
// catch events that might change the #media CSS rules
$(window).on("load resize orientationchange", function() {
mq = parseInt(window.getComputedStyle(document.documentElement,':after').getPropertyValue('content').replace(/['"]/g,''),10);
});
FOO.mq = function() { // function that just returns the scoped variable
return mq;
}
});
Try to use a timeout:
this.init = function () {
var timeout;
$(window).on("load resize orientationchange", function () {
clearTimeout(timeout);
timeout = setTimeout(function(){
if (FOO.mq() >= 480) {
DO SOMETHING
}
},15);//even 0 should be enough
});
};