This is part of a larger program which is handling a scrollbar on a <div> when modifying the height.
When logging the output of various values for moving the scrollbar, there's an issue occurring where values are being produced with decimal places, but only on Opera (version 44.0.2510.1449), and this is only happening on my friend's browser. On my own Opera (version 44.0.2510.1449) I do not encounter the same problem.
Though it's probably irrelevant, the purpose of the code is to find out where the scrollbar is in the div with id #mydiv and do something based on the result.
Similar code with changed variable names:
var myDivHeight = $('#mydiv').height();
$('#mydiv').height(myDivHeight + 50); //10 extra for padding
var scrollTop = $('#mydiv').scrollTop();
var scrollHeight = $('#mydiv').prop('scrollHeight');
console.log(scrollHeight + '-' + scrollTop + '=' + (scrollHeight - scrollTop));
console.log(myDivHeight + 60);
Note: 60 is due to the changes made to the page dynamically, so the div height has been changed. The result of the output should be that scrollHeight - scrollTop = myDivHeight + 60.
Here's my friend's console output on Opera (giving fractional scrollTop):
Here's my console output on Opera:
Here's the console output on Chrome:
Here's the console output from Firefox:
I can't find anyone else reporting this. Has this been reported or seen by anyone else? Is there any way to overcome this?
Thank you.
It turns out that taking the time to ask the question helped me answer it on my own.
First of all, to overcome the problem, it's a case of using Math.round(scrollTop.value).
The specification for scrollTop shows it is a unrestricted double data type so Opera is handling this within spec. Reference: https://drafts.csswg.org/cssom-view/#dom-element-scrolltop
An issue where this came up in jQuery before the type was changed from integer to Number: https://github.com/jquery/api.jquery.com/issues/608
This question already has answers here:
Is there a more accurate way to create a Javascript timer than setTimeout?
(16 answers)
Closed 9 years ago.
I need to reproduce an animation-like effect on the browser (mobile and desktop), this effect is just a procedural changes on the background color from black to white.
The timming is important, since a black background for a short time means "0" and a longer time means "1" (is like a binary Morse code). This information is interpreted by a light sensor in front of the screen. The sensor is very fast, and if we use a lower frequency we wont have problems with the screen (assuming a maximum refresh rate of 50Hz).
This is the script im using:
var i = 0;
var b0t = 100;
var b1t = 3.5 * b0t;
var wl = 100;
function pulses (){
bytes = "1001101010110101001000100";
bit = bytes[i];
i += 1;
document.body.style.backgroundColor = 'white';
document.body.style.color = 'white';
setTimeout(toblack, wl);
if(i <= bytes.length) {
if (bit == 1)
setTimeout(pulses, b1t + wl);
else
setTimeout(pulses, b0t + wl);
}
}
function toblack() {
document.body.style.backgroundColor = 'black';
document.body.style.color = 'black';
}
pulses ();
And a working demo (Do not open if you suffer any epileptic syndrome)
The problem is, 1 out of 20 the browser mess the timing, of course in the sensor side i have a tolerance range, a very tolerant range, but again the browser some times is out by several milliseconds, and this is no surprise, if your computer of cellphone is doing something in the background the performance of the browser is severely affected.
Is there a software solution for this? I was thinking using flash to do the flickering with more precision, but i really like my app to be accessible also from IOS. Or i can make a native app that for sure will be more precise than the browser, but then again, i will like to know if i can make it work on the browser.
the only thing i can suggest you to try is, forget about sestTimeout and try Date().getTime() it will give you a microsecond unix timestamp and you can try to query that and implement your own set timeout... i dont know if that will work, but you lost nothing trying.
take a look here http://www.sitepoint.com/creating-accurate-timers-in-javascript/
there are some examples.
The problem I am having is with this code:
function scrollLeft() {
document.body.scrollLeft -= scrollSpeed;
}
It works perfectly in Chrome and Safari but in IE and Firefox it is doing nothing. I have narrowed this down to the fact that in Firefox and IE standards mode they accept document.documentElement.scrollLeft instead of document.body.scrollLeftand my page is being rendered in standards mode.
Please note that I run this script at an interval of 10ms so I can't afford to have a large clunky detection script running each time this function needs to perform.
Does anyone know a cross browser way or an extremely light way of overcoming this? I am using the jQuery library in this script but Firefox and IE aren't recognising $('body').scrollLeft() either and I'm not sure why that is.
KISS: use scrollBy
window.scrollBy( -scrollSpeed, 0 )
Use the logical or operator to fall back.
document[body || documentElement].scrollLeft -= scrollSpeed
This is very quick, so very little overhead for you.
For bonus points do this instead
scrollLeft = (function () {
var docEl = document[body || documentElement];
return function () {
docEl.scrollLeft -= scrollSpeed;
};
})();
This only finds the relevant element reference once and closes over the temporary variable that holds it.
I'm having difficulty in explaining why the following code runs slower in IE9 than IE8!
Here's some test code that runs smoothly in about half a second in (latest) FF/Chrome/Safari on OS X, WinXP, and Win7 plus IE7 & 8 on WinXP (for simplicity I removed the tweak that makes it work in IE6).
For some reason I can't explain, it's awful in IE9, slow and clunky. Reducing the time for the setTimeout makes it a bit quicker but no less jerky.
I've tried removing and benchmarking a number of what might be the choke points (Math.min for example ... all with no change.
I'm stumped ... can anyone please point me in the right direction?... preferably one that does not require browser-sniffing?
Here's the test code ...
<div id = 'panel' class='noShow' style='background-color: #aaa;'>
<div id = 'wrapper' class='slideWrapper'>
<p>xxxxxxxxxxx</p><p>xxxxxxxxxxx</p><p>xxxxxxxxxxx</p><p>xxxxxxxxxxx</p><p>xxxxxxxxxxx</p><p>xxxxxxxxxxx</p><p>xxxxxxxxxxx</p><p>xxxxxxxxxxx</p><p>xxxxxxxxxxx</p><p>xxxxxxxxxxx</p>
</div>
</div>
<script type = 'text/javaScript'>
var e = document.getElementById('panel');
var w = document.getElementById('wrapper');
w.style.overflow = 'hidden';
w.style.height = '1px';
var sh = w.scrollHeight;
show();
function show()
{
setTimeout(function()
{
w.style.height = Math.min(sh, (w.offsetHeight + Math.ceil(sh/15))) + 'px';
if(
(w.offsetHeight < sh)
)
{
show(e);
}
else
{
w.style.height = 'auto';
}
}, 20);
}
</script>
IE9 defaults to software rendering on VMware Virtual Machines (as of 7/8/2011) there is supposed to be a hotfix released in the near future but no timeline is given per this blog post.
Are you running the 64 bit version of IE 9 or the 32 bit version? The 64 bit version is not running Chakra which is the optimized JavaScript engine from the 32 bit version
There are always odd performance variations when running in a virtualized environment. The selection of drivers and how the software offloads the processing will have an impact. Have you tried updating the drivers on your system?
I'm working on a kind of unique app which needs to generate images at specific resolutions according to the device they are displayed on. So the output is different on a regular Windows browser (96ppi), iPhone (163ppi), Android G1 (180ppi), and other devices. I'm wondering if there's a way to detect this automatically.
My initial research seems to say no. The only suggestion I've seen is to make an element whose width is specified as "1in" in CSS, then check its offsetWidth (see also How to access screen display’s DPI settings via javascript?). Makes sense, but iPhone is lying to me with that technique, saying it's 96ppi.
Another approach might be to get the dimensions of the display in inches and then divide by the width in pixels, but I'm not sure how to do that either.
<div id='testdiv' style='height: 1in; left: -100%; position: absolute; top: -100%; width: 1in;'></div>
<script type='text/javascript'>
var devicePixelRatio = window.devicePixelRatio || 1;
dpi_x = document.getElementById('testdiv').offsetWidth * devicePixelRatio;
dpi_y = document.getElementById('testdiv').offsetHeight * devicePixelRatio;
console.log(dpi_x, dpi_y);
</script>
grabbed from here http://www.infobyip.com/detectmonitordpi.php. Works on mobile devices! (android 4.2.2 tested)
I came up with a way that doesn't require the DOM... at all
The DOM can be messy, requiring you to append stuff to the body without knowing what stuff is going on with width: x !important in your stylesheet. You would also have to wait for the DOM to be ready to use...
/**
* Binary search for a max value without knowing the exact value, only that it can be under or over
* It dose not test every number but instead looks for 1,2,4,8,16,32,64,128,96,95 to figure out that
* you thought about #96 from 0-infinity
*
* #example findFirstPositive(x => matchMedia(`(max-resolution: ${x}dpi)`).matches)
* #author Jimmy Wärting
* #see {#link https://stackoverflow.com/a/35941703/1008999}
* #param {function} fn The function to run the test on (should return truthy or falsy values)
* #param {number} start=1 Where to start looking from
* #param {function} _ (private)
* #returns {number} Intenger
*/
function findFirstPositive (f,b=1,d=(e,g,c)=>g<e?-1:0<f(c=e+g>>>1)?c==e||0>=f(c-1)?c:d(e,c-1):d(c+1,g)) {
for (;0>=f(b);b<<=1);return d(b>>>1,b)|0
}
var dpi = findFirstPositive(x => matchMedia(`(max-resolution: ${x}dpi)`).matches)
console.log(dpi)
There is the resolution CSS media query — it allows you to limit CSS styles to specific resolutions:
http://www.w3.org/TR/css3-mediaqueries/#resolution
However, it’s only supported by Firefox 3.5 and above, Opera 9 and above, and IE 9. Other browsers won’t apply your resolution-specific styles at all (although I haven’t checked non-desktop browsers).
Here is what works for me (but didn't test it on mobile phones):
<body><div id="ppitest" style="width:1in;visible:hidden;padding:0px"></div></body>
Then I put in the .js: screenPPI = document.getElementById('ppitest').offsetWidth;
This got me 96, which corresponds to my system's ppi.
DPI is by definition tied to the physical size of the display. So you won't be able to have the real DPI without knowing exactly the hardware behind.
Modern OSes agreed on a common value in order to have compatible displays: 96 dpi. That's a shame but that's a fact.
You will have to rely on sniffing in order to be able to guess the real screen size needed to compute the resolution (DPI = PixelSize / ScreenSize).
I also needed to display the same image at the same size at different screen dpi but only for Windows IE. I used:
<img src="image.jpg" style="
height:expression(scale(438, 192));
width:expression(scale(270, 192))" />
function scale(x, dpi) {
// dpi is for orignal dimensions of the image
return x * screen.deviceXDPI/dpi;
}
In this case the original image width/height are 270 and 438 and the image was developed on 192dpi screen. screen.deviceXDPI is not defined in Chrome and the scale function would need to be updated to support browsers other than IE
The reply from #Endless is pretty good, but not readable at all,
this is a similar approche with fixed min/max (it should be good ones)
var dpi = (function () {
for (var i = 56; i < 2000; i++) {
if (matchMedia("(max-resolution: " + i + "dpi)").matches === true) {
return i;
}
}
return i;
})();
matchMedia is now well supported and should give good result, see http://caniuse.com/#feat=matchmedia
Be careful the browser won't give you the exact screen dpi but only an approximation
function getPPI(){
// create an empty element
var div = document.createElement("div");
// give it an absolute size of one inch
div.style.width="1in";
// append it to the body
var body = document.getElementsByTagName("body")[0];
body.appendChild(div);
// read the computed width
var ppi = document.defaultView.getComputedStyle(div, null).getPropertyValue('width');
// remove it again
body.removeChild(div);
// and return the value
return parseFloat(ppi);
}
(From VodaFone)
Reading through all these responses was quite frustrating, when the only correct answer is: No, it is not possible to detect the DPI from JavaScript/CSS. Often, the operating system itself does not even know the DPI of the connected screens (and reports it as 96 dpi, which I suspect might be the reason why many people seem to believe that their method of detecting DPI in JavaScript is accurate). Also, when multiple screens are connected to a device forming a unified display, the viewport and even a single DOM element can span multiple screens with different DPIs, which would make these calculations quite challenging.
Most of the methods described in the other answers will almost always result in an output of 96 dpi, even though most screens nowadays have a higher DPI. For example, the screen of my ThinkPad T14 has 157 dpi, according to this calculator, but all the methods described here and my operating system tell me that it has 96 dpi.
Your idea of assigning a CSS width of 1in to a DOM element does not work. It seems that a CSS inch is defined as 96 CSS pixels. By my understanding, a CSS pixel is defined as a pixel multiplied by the devicePixelRatio, which traditionally is 1, but can be higher or lower depending on the zoom level configured in the graphical interface of the operating system and in the browser.
It seems that the approach of using resolution media queries produces at least some results on a few devices, but they are often still off by a factor of more than 2. Still, on most devices this approach also results in a value of 96 dpi.
I think your best approach is to combine the suggestion of the "sniffer" image with a matrix of known DPIs for devices (via user agent and other methods). It won't be exact and will be a pain to maintain, but without knowing more about the app you're trying to make that's the best suggestion I can offer.
Can't you do anything else? For instance, if you are generating an image to be recognized by a camera (i.e. you run your program, swipe your cellphone across a camera, magic happens), can't you use something size-independent?
If this is an application to be deployed in controlled environments, can you provide a calibration utility? (you could make something simple like print business cards with a small ruler in it, use it during the calibration process).
I just found this link: http://dpi.lv/. Basically it is a webtool to discover the client device resolution, dpi, and screen size.
I visited on my computer and mobile phone and it provides the correct resolution and DPI for me. There is a github repo for it, so you can see how it works.
Generate a list of known DPI:
https://stackoverflow.com/a/6793227
Detect the exact device. Using something like:
navigator.userAgent.toLowerCase();
For example, when detecting mobile:
window.isMobile=/iphone|ipod|ipad|android|blackberry|opera mini|opera mobi|skyfire|maemo|windows phone|palm|iemobile|symbian|symbianos|fennec/i.test(navigator.userAgent.toLowerCase());
And profit!
Readable code from #Endless reply:
const dpi = (function () {
let i = 1;
while ( !hasMatch(i) ) i *= 2;
function getValue(start, end) {
if (start > end) return -1;
let average = (start + end) / 2;
if ( hasMatch(average) ) {
if ( start == average || !hasMatch(average - 1) ) {
return average;
} else {
return getValue(start, average - 1);
}
} else {
return getValue(average + 1, end);
}
}
function hasMatch(x) {
return matchMedia(`(max-resolution: ${x}dpi)`).matches;
}
return getValue(i / 2, i) | 0;
})();
Maybe I'm a little bit steering off this topic...
I was working on a html canvas project, which was intended to provide a drawing canvas for people to draw lines on. I wanted to set canvas's size to 198x280mm which is fit for A4 printing.
So I started to search for a resolution to convert 'mm' to 'px' and to display the canvas suitably on both PC and mobile.
I tried solution from #Endless ,code as:
const canvas = document.getElementById("canvas");
function findFirstPositive(b, a, i, c) {
c=(d,e)=>e>=d?(a=d+(e-d)/2,0<b(a)&&(a==d||0>=b(a-1))?a:0>=b(a)?c(a+1,e):c(d,a-1)):-1
for (i = 1; 0 >= b(i);) i *= 2
return c(i / 2, i)|0
}
const dpi = findFirstPositive(x => matchMedia(`(max-resolution: ${x}dpi)`).matches)
let w = 198 * dpi / 25.4;
let h = 280 * dpi / 25.4;
canvas.width = w;
canvas.height = h;
It worked well on PC browser, showing dpi=96 and size was 748x1058 px;work well on PC
However turned to mobile devices, it was much larger than I expected: size: 1902x2689 px.can't work on mobile
After searching for keywords like devicePixelRatio, I suddenly realize that, I don't actually need to show real A4 size on mobile screen (under which situation it's actually hard to use), I just need the canvas's size fit for printing, so I simply set the size to:
let [w,h] = [748,1058];
canvas.width = w;
canvas.height = h;
...and it is well printed:well printed