Related
I'm working on a simulation of the famous Prague astronomical clock. It's only partially done, but the work-in-progress can be seen here: https://shetline.com/orloj/
The page layout consists of an SVG image of the clock, plus a control panel for setting date, time, longitude, and latitude. The clock needs to expand to fill available space. The control panel is close to a fixed size, but can shrink a bit for smaller displays.
What I want (and I think should be MUCH easier to do) is for the contents of the web page to display nicely and neatly like this:
The tricky thing has been getting the layout to use available space well, but without needing to be scrolled, and without any components being clipped or hidden. What was especially difficult was automatically sizing things regardless of whether "chrome" (i.e. address and navigation bars) was being displayed or not.
Simple solutions only worked partially for me -- the user might have to pinch and scroll to get things right, manually hide toolbars, forcibly refresh after changing from landscape to portrait orientation, etc.
These are the difficulties I ran into:
With mobile browsers like Safari, 100vh was taller than window.innerHeight, so if I scaled using vh units, parts of what I wanted to display would be cut off until the user manually hid the toolbars. In desktop browsers, however, 100vh and window.innerHeight were always in sync.
Orientation changes weren't consistently reported. Safari didn't generate any resize events when the orientation of my phone was changed, only now-deprecated orientationchanged events. On one Android tablet I experimented with, orientation changes produced no browser events at all that I was aware of at all.
When the orientation changed, window.screen.width and window.screen.height weren't swapped as I would have expected... unless I was using the Chrome console responsive layout test.
After receiving resize or orientationchanged events, some of the values I needed to check like window.innerHeight weren't "settled" yet, so I needed to use setTimeouts to recheck if values changed over time.
When a user manually zooms into the display, the zoom state is sort of "sticky" and can keep the layout from returning to the preferred state after orientation changes.
All of this meant coming up with a bunch of ad hoc hackery to get the results I wanted. But this seems like such a basic result kind of layout effect to want to get, I can't help but wondering if I've made this much harder than it should be, and there isn't a much easier solution that I'm missing.
Here's the code I used to solve this layout:
<meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1, maximum-scale=1">
:root {
--mfh: 100vh; // mobile full height
--mvh: 1vh; // mobile vh-like unit
}
html {
height: 100%;
margin: 0;
overflow: hidden;
width: 100%;
}
body {
background-color: #4d4d4d;
font: 14px sans-serif;
height: var(--mfh);
left: 0;
margin: 0;
position: absolute;
top: 0;
width: 100vw;
}
// Get scrolling, zooming, and panning back if display is very small:
#media screen and (orientation:landscape) and (max-height: 340px),
screen and (orientation:landscape) and (max-width: 630px),
screen and (orientation:portrait) and (max-width: 360px),
screen and (orientation:portrait) and (max-height: 740px)
{
html {
overflow: auto;
}
body {
position: static;
}
}
const docElem = document.documentElement;
const doResize = (): void => {
setTimeout(() => {
const height = window.innerHeight;
const disallowScroll = docElem.style.overflow === 'hidden';
docElem.style.setProperty('--mfh', height + 'px');
docElem.style.setProperty('--mvh', (height * 0.01) + 'px');
if (this.lastHeight !== height) {
this.lastHeight = height;
if (disallowScroll && (docElem.scrollTop !== 0 || docElem.scrollLeft !== 0)) {
docElem.scrollTo(0, 0);
setTimeout(doResize, 50);
}
else
this.updateGlobe();
}
});
};
let lastW = window.innerWidth;
let lastH = window.innerHeight;
const poll = (): void => {
const w = window.innerWidth;
const h = window.innerHeight;
const disallowScroll = docElem.style.overflow === 'hidden';
if (lastW !== w || lastH !== h || (disallowScroll && (docElem.scrollTop !== 0 || docElem.scrollLeft !== 0))) {
lastW = w;
lastH = h;
doResize();
}
setTimeout(poll, 100);
};
poll();
doResize();
Full code is here: https://github.com/kshetline/prague-clock
I'm following a guide that allows Google Map screen to disable scrolling depending on the screen size. The only part i'm struggling is to write a code that dynamically changes the True/False value when i resize the screen manually.
This is the website that I followed the instruction but I can't seem to write the correct syntax code to produce the dynamic true false value depending on the screen size https://coderwall.com/p/pgm8xa/disable-google-maps-scrolling-on-mobile-layout
Part of the code that i need to use:
$(window).resize()
And then:
setOptions()
So I'm struggling to combine them together.
I have tried something like this:
var dragging = $(window).width(function resize() {
if (dragging > 560) {
return true;
} else {
return false;
}
});
The article you linked to is lacking important information as it fails to mention that $ is (presumably) jQuery. But you don't need jQuery at all.
What you can use instead is the MediaQueryList. It is similar to media queries in CSS, but it is a JavaScript API.
The following is an untested example of how you might use it with a MediaQueryList event listener. It sets the initial value and listens to changes to your media query with a handler that uses setOptions from the Google Maps API.
var mql = window.matchMedia('(min-width: 560px)');
var isDraggable = mql.matches;
var map;
function initMap() {
map = new google.maps.Map(document.getElementById('map'), {
draggable: isDraggable
});
}
function mqChange(e) {
map.setOptions({draggable: !!e.matches});
}
mql.addListener(mqChange);
You could add an event listener to the resize event and set a value of your variable whenever the size of the window is changed:
var dragging = false;
window.addEventListener('resize', function(event) {
dragging = window.innerWidth > 560;
});
Since you mentioned that you want to disable scrolling when the windows size extends a certain value, it might be easier to just do this. If you try it you can see in the console that the value changes whenever you resize your window):
window.addEventListener('resize', function(event) {
console.log(window.innerWidth);
if (window.innerWidth > 560) {
// disable scrolling or do whatever you want to do
}
});
BTW, in your code you do this:
if (dragging > 560) {
return true;
} else {
return false;
}
You can simplify this to:
return dragging > 560
Which is exactly the same.
You can use this function to get the width and height on a resize of the screen.
$(window).resize(function() {
$windowWidth = $(window).width();
$windowHeight = $(window).height();
// run other functions or code
});
But, if you want to only show/hide a html element based on the screen size, you can also use plain html/css.
<div id="maps"></div>
Css:
#media only screen and (max-width: 560px) {
#maps {
display: none;
}
}
you can use the matchMedia function to run a callback whenever the media query status is changing
var mql = window.matchMedia('(min-width: 700px)');
function mediaHandler(e) {
if (e.matches) {
/* the viewport is more than 700 pixels wide */
} else {
/* the viewport is 700 pixels wide or less */
}
}
mql.addListener(mediaHandler);
I have two working JSFiddle which I want to combine and work together.
JSFiddle-1 : http://jsfiddle.net/MYSVL/3050/
window.onresize=function() {
var child = $('.artist');
var parent = child.parent();
child.css('font-size', '18px');
while( child.height() > parent.height() ) {
child.css('font-size', (parseInt(child.css('font-size')) - 1) + "px");
}
Here the text inside artist container is responsive and its stretching to maximum font size when the screen is bigger. Its also shrinking to smallest font size when screen size is smaller.
JSFiddle-2 : http://jsfiddle.net/MYSVL/3047/
function calcDivHeights() {
window.onresize=$(".artist").each(function () {
var child = $(this);
var parent = child.parent();
//child.css('font-size', '18px');
while (child.height() > parent.height()) {
child.css('font-size', (parseInt(child.css('font-size')) - 1) + "px");
}
});
}
Here the function is checking for every artist div and adjusting the font size according to the while condition but I am unable to make it responsive like my JSFiddle-1 . Once the text size is smaller it remains smaller even I make the screen bigger. I want my JSFiddle-2 to work exactly as JSFiddle-1 so that I can maintain the responsiveness of the text according to screen size.
Can someone please help me out or feel free to modify my JSFiddle-2 in order to achieve the goal.
Thanks in advance
I can't see differences between your two fiddles except for the commented child.css('font-size', '18px'), both should do the same thing.
Your second fiddle seems to not works properly because once you resize window to a smaller resolution, child becomes smaller or equal to parent. Then, when you return on bigger resolution, you call again while( child.height() > parent.height() ), but now your child height is not greater than your parent height.
I think the following will just do what you're asking for:
$(document).ready(function () {
function adjustFontSize() {
var child = $('.artist');
var parentHeight = child.parent().height();
while( child.height() > parentHeight ) {
child.css('font-size', (parseInt(child.css('font-size')) - 1) + "px");
}
while( child.height() < parentHeight ) {
child.css('font-size', (parseInt(child.css('font-size')) + 1) + "px");
}
};
adjustFontSize(); // Call it when document is ready
window.onresize = adjustFontSize; // Call it each time window resizes
});
.artist-container {
width: 20%;
height: 40px;
border: 1px solid black;
overflow: hidden;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="artist-container">
<div class="artist">Audhit Audhit Audhit Audhit Audhit Audhit Audhit</div>
</div><br>
<div class="artist-container">
<div class="artist">Lorem</div>
</div><br>
I think the CSS media queries approach is so much better for making a web responsive, than calculating everything with Javascript all the time.
For example, you can think about three sizes:
Phone (lets say, 400px width)
Tablet (lets say, 800px width)
Computer (lets say, >800px width)
One way of achieving this, using the desktop first approach, would be:
/* DESKTOP */
body {
font-size: 1em;
...
}
...
/* General rules for desktop
...
/* TABLET */
#media screen and (max-width:800px and min-width:400px) {
/* Overwrite the rules you want to change */
body {
font-size: .75em;
}
...
}
/* PHONE */
#media screen and (max-width:400px) {
/* Overwrite the rules you want to change */
body {
font-size: .75em;
}
#div1 {
height: 20%;
}
...
}
In addition to this, don't forget to work with relative units (em, vw, vh, rem...) and percentages.
Here you have a really good link about responsive design.
Note: Ok while I was typing this question I came across this
question which suggests to use #media query but was asked back in
2011...
As you know CSS3 introduces new Viewport-percentage length units, vh and vw, which I feel are really useful for a solid responsive layout, so my question is, is there any JavaScript/jQuery alternative for this? More over apart from using it for font sizes, is it safe to use for sizing elements? Like example
div {
height: 6vh;
width: 20vh; /* Note am using vh for both, do I need to use vw for width here? */
}
Update 5: .css(property) fix
Plugins like fancyBox use .css('margin-right') to fetch the right margin of an element and .css('margin-right', '12px') to set the right margin of an element. This was broken, because there was no check if props is a string and if there are multiple arguments given. Fixed it by checking if props is a string. If so and there is multiple arguments, arguments is rewritten into an object, otherwise parseProps( $.extend( {}, props ) ) is not used.
Update 4: Plugin for responsive layouts https://github.com/elclanrs/jquery.columns (in the works)
I gave this a (long) try. First here's the CSS example: http://jsbin.com/orajac/1/edit#css. (resize the output panel). Notice that the font-size doesn't work with viewport units, at least on latest Chrome.
And here's my attempt at doing this with jQuery. The jQuery demo which works with the font as well is at http://jsbin.com/izosuy/1/edit#javascript. Haven't tested it extensively but it seems to work with most properties since it's just converting the values to pixel and then by calling the plugin on window.resize it keeps updating.
Update: Updated code to work with many browsers. Test locally if you're using anything other than Chrome because jsBin acts a bit weird with window.resize.
Update 2: Extend native css method.
Update 3: Handle window.resize event inside of the plugin so the integration is now seamless.
The gist (to test locally): https://gist.github.com/4341016
/*
* CSS viewport units with jQuery
* http://www.w3.org/TR/css3-values/#viewport-relative-lengths
*/
;(function( $, window ){
var $win = $(window)
, _css = $.fn.css;
function viewportToPixel( val ) {
var percent = val.match(/[\d.]+/)[0] / 100
, unit = val.match(/[vwh]+/)[0];
return (unit == 'vh' ? $win.height() : $win.width()) * percent +'px';
}
function parseProps( props ) {
var p, prop;
for ( p in props ) {
prop = props[ p ];
if ( /[vwh]$/.test( prop ) ) {
props[ p ] = viewportToPixel( prop );
}
}
return props;
}
$.fn.css = function( props ) {
var self = this
, originalArguments = arguments
, update = function() {
if ( typeof props === 'string' || props instanceof String ) {
if (originalArguments.length > 1) {
var argumentsObject = {};
argumentsObject[originalArguments[0]] = originalArguments[1];
return _css.call(self, parseProps($.extend({}, argumentsObject)));
} else {
return _css.call( self, props );
}
} else {
return _css.call( self, parseProps( $.extend( {}, props ) ) );
}
};
$win.resize( update ).resize();
return update();
};
}( jQuery, window ));
// Usage:
$('div').css({
height: '50vh',
width: '50vw',
marginTop: '25vh',
marginLeft: '25vw',
fontSize: '10vw'
});
I am facing this issue with the Android 4.3 stock browser (doesn't support vw,vh, etc).
The way I solved this is using 'rem' as a font-size unit and dynamically changing the < html >'s font-size with javascript
function viewport() {
var e = window, a = 'inner';
if (!('innerWidth' in window )) {
a = 'client';
e = document.documentElement || document.body;
}
return { width : e[ a+'Width' ] , height : e[ a+'Height' ] };
}
jQuery(window).resize(function(){
var vw = (viewport().width/100);
jQuery('html').css({
'font-size' : vw + 'px'
});
});
and in your css you can use 'rem' instead of px,ems,etc
.element {
font-size: 2.5rem; /* this is equivalent to 2.5vw */
}
Here's a demo of the code : http://jsfiddle.net/4ut3e/
I wrote small helper to deal with this problem. It's supported on all main browsers and uses jQuery.
Here it is:
SupportVhVw.js
function SupportVhVw() {
this.setVh = function(name, vh) {
jQuery(window).resize( function(event) {
scaleVh(name, vh);
});
scaleVh(name, vh);
}
this.setVw = function(name, vw) {
jQuery(window).resize( function(event) {
scaleVw(name, vw);
});
scaleVw(name, vw);
}
var scaleVw = function(name, vw) {
var scrWidth = jQuery(document).width();
var px = (scrWidth * vw) / 100;
var fontSize = jQuery(name).css('font-size', px + "px");
}
var scaleVh = function(name, vh) {
var scrHeight = jQuery(document).height();
var px = (scrHeight * vh) / 100;
var fontSize = jQuery(name).css('font-size', px + "px");
}
};
Simple example how to use it in HTML:
<head>
<title>Example</title>
<!-- Import all libraries -->
<script type="text/javascript" src="js/libs/jquery-1.10.2.min.js"></script>
<script type="text/javascript" src="js/libs/SupportVhVw.js"></script>
</head>
<body>
<div id="textOne">Example text one (vh5)</div>
<div id="textTwo">Example text two (vw3)</div>
<div id="textThree" class="textMain">Example text three (vh4)</div>
<div id="textFour" class="textMain">Example text four (vh4)</div>
<script type="text/javascript">
// Init object
var supportVhVw = new SupportVhVw();
// Scale all texts
supportVhVw.setVh("#textOne", 5);
supportVhVw.setVw("#textTwo", 3);
supportVhVw.setVh(".textMain", 4);
</script>
</body>
It's available on GitHub:
https://github.com/kgadzinowski/Support-Css-Vh-Vw
Example on JSFiddle:
http://jsfiddle.net/5MMWJ/2/
Vminpoly is the only polyfill I know of — it's under develpment but works as of this post. There are static polyfills as part of the Jquery Columns and -prefix-free projects as well.
I've just made a very ugly, but perfectly working workaround for this WITH PURE CSS (no JS needed).
I came across a CSS stylesheet full of 'vw' declarations (also for heights and top/bottom properties) that needed to be rewritten for native Android Browser (which in versions 4.3 and below does NOT support 'vw' units).
Instead of rewriting everything to percentages, that are relative to parent's width (so the deeper in DOM, the most complicated calculations), which would give me a headache even before I would reach the first { height: Xvw } declaration, I generated myself the following stylesheet:
http://splendige.pl/vw2rem.css
I guess you're all familiar with 'em' units (if not: http://www.w3schools.com/cssref/css_units.asp). After some testing I discovered that old Android Browsers (as well as all the others) perfectly support the 'rem' units, which work the same as 'em', but are relative to the ROOT element font-size declaration (in most cases, the html tag).
So the easiest way to make this work was to declare the font-size for html tag that is equal to 1% of the viewport's width, e.g. 19.2px for 1920px viewport and use a lot of media queries for the whole 1920-320px range. This way I made the 'rem' unit equal to 'vw' in all resolutions (step is 10px, but you can even try declaring html{font-size} for every 1px). Then I just batch-replaced 'vw' with 'rem' and admired the working layout on Android 4.3 native browser.
Whatsmore, you can then redeclare the font-size even for the whole body tag (in whatever units you want: px, em, pt, etc.) and it does NOT affect the 'rem' equality to 'vw' in the whole stylesheet.
Hope this helps. I know it looks kinda silly, but works like a charm. Remember: if something looks stupid, but works, it is not stupid :)
"jquery.columns" is so far the best solutions.
https://github.com/elclanrs/jquery.columns
You only need 2 lines of codes to turn "vh" into a "all-browser scale".
Declare a class in html:
<img src="example.jpg" class="width50vh" />
Then in javascript:
$('.width50vh').css({width: '50vw'});
The simplest and most elegant solution I have found is to simply make a div that has height: 1vh; and width: 1vw; and when the page loads grab those values as pixels with getBoundingClientRect(). then add a listener to the document to listen for screen resizing and update the size when screen is resized.
I save the value in pixels of the vh and vw and then whenever I need to use vh i would just say 100 * vh which would give me 100vh.
Here is a code snippet on it's implementation, my answer is in vanilla js, no need for jquery.
function getViewportUnits(){
const placeholder = document.getElementById("placeholder");
const vh = placeholder.getBoundingClientRect().height;
const vw = placeholder.getBoundingClientRect().width;
console.log({vh: vh, vw: vw});
}
#placeholder{
background: red;
height: 1vh;
width: 1vw;
}
<body onload="getViewportUnits()">
<div id="placeholder"></div>
I've published a tiny lib that eases viewport-relative dimensions usage. Keep in mind it's not a polyfill, so it requires that you apply classes on the elements you want to resize. For instance, <div class="vh10 vw30">hello</div> will fill 10% of the height and 30% of the width.
Check it out: https://github.com/joaocunha/v-unit
Is it possible to detect, using JavaScript, when the user changes the zoom in a page?
I simply want to catch a "zoom" event and respond to it (similar to window.onresize event).
Thanks.
There's no way to actively detect if there's a zoom. I found a good entry here on how you can attempt to implement it.
I’ve found two ways of detecting the
zoom level. One way to detect zoom
level changes relies on the fact that
percentage values are not zoomed. A
percentage value is relative to the
viewport width, and thus unaffected by
page zoom. If you insert two elements,
one with a position in percentages,
and one with the same position in
pixels, they’ll move apart when the
page is zoomed. Find the ratio between
the positions of both elements and
you’ve got the zoom level. See test
case.
http://web.archive.org/web/20080723161031/http://novemberborn.net/javascript/page-zoom-ff3
You could also do it using the tools of the above post. The problem is you're more or less making educated guesses on whether or not the page has zoomed. This will work better in some browsers than other.
There's no way to tell if the page is zoomed if they load your page while zoomed.
Lets define px_ratio as below:
px ratio = ratio of physical pixel to css px.
if any one zoom The Page, the viewport pxes (px is different from pixel ) reduces and should be fit to The screen so the ratio (physical pixel / CSS_px ) must get bigger.
but in window Resizing, screen size reduces as well as pxes. so the ratio will maintain.
zooming: trigger windows.resize event --> and change px_ratio
but
resizing: trigger windows.resize event --> doesn’t change px_ratio
//for zoom detection
px_ratio = window.devicePixelRatio || window.screen.availWidth / document.documentElement.clientWidth;
$(window).resize(function(){isZooming();});
function isZooming(){
var newPx_ratio = window.devicePixelRatio || window.screen.availWidth / document.documentElement.clientWidth;
if(newPx_ratio != px_ratio){
px_ratio = newPx_ratio;
console.log("zooming");
return true;
}else{
console.log("just resizing");
return false;
}
}
The key point is difference between CSS PX and Physical Pixel.
https://gist.github.com/abilogos/66aba96bb0fb27ab3ed4a13245817d1e
Good news everyone some people! Newer browsers will trigger a window resize event when the zoom is changed.
I'm using this piece of JavaScript to react to Zoom "events".
It polls the window width.
(As somewhat suggested on this page (which Ian Elliott linked to): http://novemberborn.net/javascript/page-zoom-ff3 [archive])
Tested with Chrome, Firefox 3.6 and Opera, not IE.
Regards, Magnus
var zoomListeners = [];
(function(){
// Poll the pixel width of the window; invoke zoom listeners
// if the width has been changed.
var lastWidth = 0;
function pollZoomFireEvent() {
var widthNow = jQuery(window).width();
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);
})();
This works for me:
var deviceXDPI = screen.deviceXDPI;
setInterval(function(){
if(screen.deviceXDPI != deviceXDPI){
deviceXDPI = screen.deviceXDPI;
... there was a resize ...
}
}, 500);
It's only needed on IE8. All the other browsers naturally generate a resize event.
There is a nifty plugin built from yonran that can do the detection. Here is his previously answered question on StackOverflow. It works for most of the browsers. Application is as simple as this:
window.onresize = function onresize() {
var r = DetectZoom.ratios();
zoomLevel.innerHTML =
"Zoom level: " + r.zoom +
(r.zoom !== r.devicePxPerCssPx
? "; device to CSS pixel ratio: " + r.devicePxPerCssPx
: "");
}
Demo
Although this is a 9 yr old question, the problem persists!
I have been detecting resize while excluding zoom in a project, so I edited my code to make it work to detect both resize and zoom exclusive from one another. It works most of the time, so if most is good enough for your project, then this should be helpful! It detects zooming 100% of the time in what I've tested so far. The only issue is that if the user gets crazy (ie. spastically resizing the window) or the window lags it may fire as a zoom instead of a window resize.
It works by detecting a change in window.outerWidth or window.outerHeight as window resizing while detecting a change in window.innerWidth or window.innerHeight independent from window resizing as a zoom.
//init object to store window properties
var windowSize = {
w: window.outerWidth,
h: window.outerHeight,
iw: window.innerWidth,
ih: window.innerHeight
};
window.addEventListener("resize", function() {
//if window resizes
if (window.outerWidth !== windowSize.w || window.outerHeight !== windowSize.h) {
windowSize.w = window.outerWidth; // update object with current window properties
windowSize.h = window.outerHeight;
windowSize.iw = window.innerWidth;
windowSize.ih = window.innerHeight;
console.log("you're resizing"); //output
}
//if the window doesn't resize but the content inside does by + or - 5%
else if (window.innerWidth + window.innerWidth * .05 < windowSize.iw ||
window.innerWidth - window.innerWidth * .05 > windowSize.iw) {
console.log("you're zooming")
windowSize.iw = window.innerWidth;
}
}, false);
Note: My solution is like KajMagnus's, but this has worked better for me.
⬤ The resize event works on modern browsers by attaching the event on window, and then reading values of thebody, or other element with for example (.getBoundingClientRect()).
In some earlier browsers it was possible to register resize event
handlers on any HTML element. It is still possible to set onresize
attributes or use addEventListener() to set a handler on any element.
However, resize events are only fired on the window object (i.e.
returned by document.defaultView). Only handlers registered on the
window object will receive resize events.
⚠️ Do resize your tab, or zoom, to trigger this snippet:
window.addEventListener("resize", getSizes, false)
function getSizes(){
let body = document.body
body.width = window.innerWidth
body.height = window.innerHeight
console.log(body.width +"px x "+ body.height + "px")
}
getSizes()
⬤ An other modern alternative: the ResizeObserver API
Depending your layout, you can watch for resizing on a particular element.
This works well on «responsive» layouts, because the container box get resized when zooming.
function watchBoxchange(e){
info.textContent = e[0].contentBoxSize[0].inlineSize+" x "+e[0].contentBoxSize[0].blockSize + "px"
}
new ResizeObserver(watchBoxchange).observe(fluid)
#fluid {
width: 200px;
height:100px;
overflow: auto;
resize: both;
border: 3px black solid;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
font-size: 8vh
}
<div id="fluid">
<info id="info"></info>
</div>
💡 Be careful to not overload javascript tasks from user gestures events. Use requestAnimationFrame whenever you needs redraws.
I'd like to suggest an improvement to previous solution with tracking changes to window width. Instead of keeping your own array of event listeners you can use existing javascript event system and trigger your own event upon width change, and bind event handlers to it.
$(window).bind('myZoomEvent', function() { ... });
function pollZoomFireEvent()
{
if ( ... width changed ... ) {
$(window).trigger('myZoomEvent');
}
}
Throttle/debounce can help with reducing the rate of calls of your handler.
According to MDN, "matchMedia" is the proper way to do this https://developer.mozilla.org/en-US/docs/Web/API/Window/devicePixelRatio#Monitoring_screen_resolution_or_zoom_level_changes
it's a bit finicky because each instance can only watch one MQ at a time, so if you're interested in any zoom level change you need to make a bunch of matchers.. but since the browser is in charge to emitting the events it's probably still more performant than polling, and you could throttle or debounce the callback or pin it to an animation frame or something - here's an implementation that seems pretty snappy, feel free to swap in _throttle or whatever if you're already depending on that.
Run the code snippet and zoom in and out in your browser, note the updated value in the markup - I only tested this in Firefox! lemme know if you see any issues.
const el = document.querySelector('#dppx')
if ('matchMedia' in window) {
function observeZoom(cb, opts) {
opts = {
// first pass for defaults - range and granularity to capture all the zoom levels in desktop firefox
ceiling: 3,
floor: 0.3,
granularity: 0.05,
...opts
}
const precision = `${opts.granularity}`.split('.')[1].length
let val = opts.floor
const vals = []
while (val <= opts.ceiling) {
vals.push(val)
val = parseFloat((val + opts.granularity).toFixed(precision))
}
// construct a number of mediamatchers and assign CB to all of them
const mqls = vals.map(v => matchMedia(`(min-resolution: ${v}dppx)`))
// poor person's throttle
const throttle = 3
let last = performance.now()
mqls.forEach(mql => mql.addListener(function() {
console.debug(this, arguments)
const now = performance.now()
if (now - last > throttle) {
cb()
last = now
}
}))
}
observeZoom(function() {
el.innerText = window.devicePixelRatio
})
} else {
el.innerText = 'unable to observe zoom level changes, matchMedia is not supported'
}
<div id='dppx'>--</div>
You can also get the text resize events, and the zoom factor by injecting a div containing at least a non-breakable space (possibly, hidden), and regularly checking its height. If the height changes, the text size has changed, (and you know how much - this also fires, incidentally, if the window gets zoomed in full-page mode, and you still will get the correct zoom factor, with the same height / height ratio).
<script>
var zoomv = function() {
if(topRightqs.style.width=='200px){
alert ("zoom");
}
};
zoomv();
</script>
On iOS 10 it is possible to add an event listener to the touchmove event and to detect, if the page is zoomed with the current event.
var prevZoomFactorX;
var prevZoomFactorY;
element.addEventListener("touchmove", (ev) => {
let zoomFactorX = document.documentElement.clientWidth / window.innerWidth;
let zoomFactorY = document.documentElement.clientHeight / window.innerHeight;
let pageHasZoom = !(zoomFactorX === 1 && zoomFactorY === 1);
if(pageHasZoom) {
// page is zoomed
if(zoomFactorX !== prevZoomFactorX || zoomFactorY !== prevZoomFactorY) {
// page is zoomed with this event
}
}
prevZoomFactorX = zoomFactorX;
prevZoomFactorY = zoomFactorY;
});
Here is a clean solution:
// polyfill window.devicePixelRatio for IE
if(!window.devicePixelRatio){
Object.defineProperty(window,'devicePixelRatio',{
enumerable: true,
configurable: true,
get:function(){
return screen.deviceXDPI/screen.logicalXDPI;
}
});
}
var oldValue=window.devicePixelRatio;
window.addEventListener('resize',function(e){
var newValue=window.devicePixelRatio;
if(newValue!==oldValue){
// TODO polyfill CustomEvent for IE
var event=new CustomEvent('devicepixelratiochange');
event.oldValue=oldValue;
event.newValue=newValue;
oldValue=newValue;
window.dispatchEvent(event);
}
});
window.addEventListener('devicepixelratiochange',function(e){
console.log('devicePixelRatio changed from '+e.oldValue+' to '+e.newValue);
});
Here is a native way (major frameworks cannot zoom in Chrome, because they dont supports passive event behaviour)
//For Google Chrome
document.addEventListener("mousewheel", event => {
console.log(`wheel`);
if(event.ctrlKey == true)
{
event.preventDefault();
if(event.deltaY > 0) {
console.log('Down');
}else {
console.log('Up');
}
}
}, { passive: false });
// For Mozilla Firefox
document.addEventListener("DOMMouseScroll", event => {
console.log(`wheel`);
if(event.ctrlKey == true)
{
event.preventDefault();
if(event.detail > 0) {
console.log('Down');
}else {
console.log('Up');
}
}
}, { passive: false });
I'am replying to a 3 year old link but I guess here's a more acceptable answer,
Create .css file as,
#media screen and (max-width: 1000px)
{
// things you want to trigger when the screen is zoomed
}
EG:-
#media screen and (max-width: 1000px)
{
.classname
{
font-size:10px;
}
}
The above code makes the size of the font '10px' when the screen is zoomed to approximately 125%. You can check for different zoom level by changing the value of '1000px'.