Related
I have this code for a canvas and a chat:
<section id="main_section">
<article id="GL">
<canvas id="GL-Surface">
</canvas>
</article>
<article id="chat">
<script src="chat.js"></script>
</article>
</section>
I want to set the width and height of the chat. It works when setting it in CSS, but I want to change the size of the chat inside of Javascript, since the canvas might get bigger. I use these commands to set the size:
document.getElementById("chat").width = w;
document.getElementById("chat").height = h;
But the chat doesn't change the size. These lines work for changing the size of the article containing the canvas. Printing out the document.getElementById("chat").width returns the right value, which is weird.
What is going on, that the same commands only work for some objects?
Since you have set the width and height in your CSS, you can only modify the style properties of the canvas to change it, i.e. canvas.style.width and canvas.style.height.
const canvas = document.querySelector('canvas');
canvas.style.width = "500px";
canvas {
width: 200px;
height: 200px;
background-color: dodgerblue;
}
<canvas></canvas>
The <script> tag doesn't belong there - it may know what the #gl element is, but it may not know what #chat is if the script executes before #chat gets defined by closing the article tag.
Move it to the bottom of the body.
I have an image, say 320 x 240 pixels in size, which I want to display doubled in size (i.e. 640 x 480 pixels) on a web page. In the past, I have done this by setting width=640 on the img tag.
However, I have just spotted the following in the HTML specification:
The dimension attributes are not intended to be used to stretch the image.
In that case, how do I double the size of an image on a web page without going against what the spec intends?
You can use CSS instead. The two methods that jump to mind are using the width or scale transformation.
E.g. img { width: 640px; }. This will keep the proper aspect ratio without specifying the height.
img {
width: 640px;
}
<img src="http://www.placehold.it/320x240">
E.g. img { transform: scale(2) }
img {
transform-origin: top left;
transform: scale(2);
}
<img src="http://www.placehold.it/320x240">
You can set the dimension of the parent image container
.imageContainer {
width: 640px;
height: 480px;
}
img {
width: 100%;
height: 100%
}
<div class="imageContainer">
<img src="https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcQqdnZQDnZOOyBGXXB23WS87IAxZ85drC-ylgrqWCqYk2aEh3Vo">
</div>
Via CSS, ideally in a style sheet, but in the style attribute if necessary.
For instance, here's your 32x32 gravatar stretched to 128x128:
.stretch-128 {
width: 128px;
height: 128px;
}
<img src="https://www.gravatar.com/avatar/6b938dc4205cd0cea4e4e68451c42a21?s=32&d=identicon&r=PG" class="stretch-128">
Naturally, any time you stretch an image like that, the quality is likely to suffer.
First I created a div with a class named divWrapper with a specific size. We then create a class for our image with a width of 100%(which spans across the divWapper), and added a height of auto(To automatically keep the height in proper proportion). You can adjust the size of the image according to the divWapper class.
.divWrapper{
width:640px;
height:480px;
float:left;
}
//Now set you image class to
.imageclass{
width:100%
height:auto;
float:left;
}
<div class="divWrapper">
<image class="imageclass" src="https://upload.wikimedia.org/wikipedia/commons/2/2d/Picea_glauca_taiga.jpg">
<div>
This will keep the image in proportion no matter what size the wrapper container is.
A Helpful Note:
You must have a high quality image from the start I suggest using vector images. I prefer to Use PNG or SVG. It has better quality than jpeg and others.
Here are a couple of link that describes a little about image extensions.
https://blog.online-convert.com/best-image-file-extensions/
https://www.sitepoint.com/gif-png-jpg-which-one-to-use/
There are at least 4 ways to do this.
1. directly set width attribute of img
principle: modify the width of the image.
set width: 640px; display: block for the img tag.
2. wrap img with a div
principle: inherit the width from parent node.
div width: 640px
img display: block; width: 100%
3. set the image as background of a div
principle: the image is no longer a img tag, but the background of a node.
div background-image: url(), use the image as background image.
div background-size: {2x width} {2x height}, to resize the size of the image
4. scale the image into 2x
set the transform property to scale(2), which means I want the size of it 2 times of its original size
tips: also, we need to set the property transform-origin into top left, in order that it can start from the position(relatively) top: 0; left: 0;
Run the snippet and have a look :)
.method-1 {
display: block;
width: 640px;
}
.method-2 {
width: 640px;
}
.method-2 img {
display: block;
width: 100%;
}
.method-3 {
width: 640px;
height: 480px;
background-image: url(http://www.placehold.it/320x240);
background-size: 640px 480px;
}
.method-4 {
transform-origin: top left;
transform: scale(2);
}
<h1>method 1: img displays as block, width 640px</h1>
<img src="http://www.placehold.it/320x240" class="method-1" />
<br/>
<h1>method 2: parent dom width 640px, img width 100%.</h1>
<div class="method-2">
<img src="http://www.placehold.it/320x240" />
</div>
<br/>
<h1>method 3: the img as div's background, use the background-size attribute to set 2x size.</h1>
<div class="method-3"></div>
<br/>
<h1>method 4: use CSS3 transform property to scale the image into 2x.</h1>
<p>tips: when we set `transform: scale(2);`, we also need to define the `transform-origin` property to `top left`, in order that the image will align from top to bottom as well as from left to right, or it will lost itself. LOL.</p>
<img src="http://www.placehold.it/320x240" class="method-4" />
Here's a list of things you want to avoid when loading an image:
FOUC - which happens when the browser does not have the image size before loading it (in which case it doesn't allocate any space for it until it's fully loaded) or telling the browser the wrong size
distortion (displaying an image at a different aspect ratio than 1:1)
pixelation (displaying an image at a significantly bigger size than its native size)
loading images with significantly bigger native size than the size in page - they slow the page down and consuming unnecessary bandwidth
There are ways to deal with each of the above.
For avoiding FOUC, there are a few methods:
have the image sizes (or ratio) saved, besides the file itself, so you can allocate the correct space in page flow, before loading the images (WP does this out of the box, for example and most themes use it to avoid FOUC)
have the image load in a container, as background image with background-size: cover, so FOUC is not possible, as the document already knows the size of the element, before the image is loaded. This technique also has the advantage of not displaying broken image anchors when an image fails to load. Also, this technique is widely used today in full screen image layouts, some of them using parallax effect. Has the disadvantage of cropping top/bottom or left/right, depending on aspect ratio difference between container and image.
load the image in a position:absolute centered container and when you have its size (and are able to calculate exact position in document flow) animate its scale from 0 to 1 while also animating its container size from "default" to proper size.
Regarding distortion, there isn't much to say, except do not do it. Any other option is better than distortion: cropping, pixelation, even FOUC. Distortion basically says: "Don't trust me with design tasks - I'm unable to tell does from donts" - which, to many, translates to: "I'm unqualified for professional frontend development. I'll mess up any website coming my way, most likely without knowing".
On pixelation. It's a tricky one: you want the page to load as fast as possible, but also want crisp images. When dealing with very large background images, a neat trick is to add a pattern over the image (stripes, dots or any other such semi-transparent patterns - an artistic touch to make pixellation a lot less noticeable).
Another good and smart technique for getting best of both worlds (speed and image quality) is to:
load a proportional thumbnail versions for every image below the fold (yes, they'll look pixelated) and display them at .65 opacity. Lazy load the full size images before they are scrolled into view. Even if the pixelated version is visible for a short while, it will be replaced by the full-size one, creating a focus-in effect, together with changing opacity to 1. If you have quality, outstanding images, you really want this effect (a) because it makes them stand out and b) because you want to load the images at proper, crisp size). The images will look "alive" and get subtle focus, leaving visitors with the impression of looking at a highly sophisticated website.
Avoid oversizing. Here src-set comes into play, combined with the thumbnail loaders described above. And, of course: lazy-loading. Always lazy load anything below the fold. It makes a big difference.
In short, that's how you correctly load images. In reality, it's a huge subject, I haven't even touched optimization, raster vs vectors, svg's or webp formats.
You can achieve this by two way as below.
<img class="xx" src="http://placehold.jp/150x150.png">
.xx {
zoom: 200%;
}
.xx {
transform: scale(2);
}
Generally I haven't found an article providing a way to scale images with client side technologies (leave it the browser's mechanism) without losing some quality.
With that being said I am afraid that If you want the best way to double your image according to HTML Standard specifications the best answer is to use a different image. A scaled up image through back-end process or through Photoshop. Now if you want adaptive image according to the user's screen since most probably this is the actual case the HTML Standard to achieve that is with the following way:
<picture>
<source srcset="http://www.placehold.it/320x240" media="(min-width: 600px)">
<source srcset="http://www.placehold.it/640x480" media="(min-width: 900px)">
<img src="http://www.placehold.it/160x120">
</picture>
Out of my experience the best way to double the size of any image for usage in websites is to actually double the image in a program like Photoshop and then upload it.
The image size, the pixel density for the retina screens, and the plethora of different screen sizes makes working with images a little more advanced than it used to be, but nothing too hard.
check the following example:
<picture>
<source
sizes="(width < 32em) contain, (width >=32em) contain 80vw calc(100vh - 10em)"
srcset="full.jpg 2048h 1024w, half.jpg 1024h, quarter.jpg 512h, eighth.jpg 256h" />
<img src="eighth.jpg" alt="rose flower!" />
</picture>
You upload a relatively big image, at least FHD, or bigger, and then with the 'sizes' and 'srcset' you configure it, which one to load depending on the screen size/type.
This way you cover any potential resolution, small or big, and you always display the best version of your image.
Ok, i admit it, there is some manual labor work to happen in every image prior the uploading, but the final result is worth it!
Now if you insist of keeping the same small image file and double it via using coding, then the best way is to add a CSS class to your image, and on that class specify your doubled dimensions, and that's it.
Here is a funny and good explanation about images srcset, sizes, and the importance of why we need to do it this way.
A fun article on srcset
You can user javascript for like this way this to change image size by it's double
Check this JsFiddle Demo
<html lang="en">
<head>
<script src="https://code.jquery.com/jquery-1.12.4.min.js"></script>
<script type="text/javascript">
$(document).ready(function(){
$("button").click(function(){
var img = $("#sky");
// Create dummy image to get real size
$("<img>").attr("src", $(img).attr("src")).load(function(){
var realWidth = this.width;
var realHeight = this.height;
alert("Original width=" + realWidth + ", " + "Original height=" + realHeight);
var newWidth = realWidth * 2;
var newHeight = realHeight * 2;
$("#sky").width(newWidth);
$("#sky").width(newHeight);
alert("new Image Size" + newWidth+"x"+newHeight);
});
});
});
</script>
</head>
<body>
<img src="https://www.cluedin.net/images/providers/stackoverflow.png" id="sky" width="250" alt="Cloudy Sky" contextmenu="skymenu">
<p><button type="button">Get Original Image Size And Double it</button></p>
</body>
</html>
I like most of the previous solutions, but the issue here is that we don't actually know the current image size to double it!!
So, my solution is kinda dynamic, and it won't affect other images on your website as well.
Also, it can be enhanced to triple their sizes, and more if you wanted!
Check a Live Preview here.
Or read it below:
HTML
<img class="resize-double" src="https://placeimg.com/100/50/nature">
<img src="https://placeimg.com/200/100/nature">
JavaScript
// Select all images to be resized
const toResizeImgs = document.querySelectorAll(".resize-double");
// Loop over those images and double their size
for(let i=0; i < toResizeImgs.length; i++) {
toResizeImgs[i].style.width = toResizeImgs[i].offsetWidth * 2 + "px";
toResizeImgs[i].style.height = toResizeImgs[i].offsetHeight + "px";
}
It would select the images with the class resize-double, loop on them and change their width/height to 2x of the current one!
NOTE: BothoffsetWidth and offsetHeight return the current width/height including Margins, Paddings and Borders. If it doesn't suit you, you can use clientWidth and clientHeight, or even scrollWidth and scrollHeight based on your needs!
The most optimal way is to load the biggest image to your site and downscale it where you need through CSS.
1) Best quality for your image since upscaling distorts the image.
2) No need to load more images with different sizes
3) Downscaling does not spoil the image and it is just some lines of CSS
If you don't want to load the bigger image, the second best and fastest solution is to upscale it through CSS. Width attribute no longer works in <img>
You can do that many various ways. But whatever you do, first download your image which size you want it to mean the double size otherwise if you double the size, then it gets pixelated or looks faded.
image{ transform: scale(2); }
Try transform scale to double sized your image
Try setting the width in the image with CSS
Try with JavaScript with setAttribute("width", "size")
Well you can use css and avoid stretching.
img{ width:640px; background-size: cover; height:auto;}
This will stretch your image equally with suitable height. You can also try setting height to a specific number.
The dimension attributes are not intended to be used to stretch the image.
As you already mentioned that using width attribute doesn't intend to stretch the image. so in that case we should use transform: scale(2); which is a good idea but this may also get conflict regarding image overlapping other content, so best idea is to use 2x width of div with image scaled 2x with css property 'transform: scale(2);'
Hence: using the below style should be a good idea.
.img2x_container {
width: 640px;
height: 480px;
}
.img2x_container img {
transform: scale(2);
}
This use case is only beneficial when we know the image height and width.
You can make use of transform -> scale
img#imagId {
-ms-transform: scale(2); /* IE 9 */
-webkit-transform: scale(2); /* Safari 3-8 */
transform: scale(2);
}
I use following Java Script code to scale image according which can fit best in container. You may make few changes per your need.
function scaleImage(container, image) {
var c = document.getElementById(container);
var i = document.getElementById(image);
var newSize = scaleSize(c.clientWidth, c.clientHeight, i.width, i.height);
i.style.width = newSize.width;
i.style.height = newSize.height;
c.style.backgroundColor = "transparent";
c.style.border = "none";
var intHeight = ((100-((parseFloat(i.style.height)/87)*100))/2);
var strHeightPercentage = intHeight.toString().concat("%");
c.style.top = strHeightPercentage;}
function scaleSize(maxW, maxH, currW, currH) {
var ratio = currH / currW;
if (currW >= maxW && ratio <= 1) {
currW = maxW;
currH = currW * ratio;
}
else if (currH >= maxH) {
currH = maxH;
currW = currH / ratio;
}
return { width: currW, height: currH };
}
CSS trick, setting just one property
If you want to use CSS, there's a really good way:
img#image_id {
width: calc(2 * 320px);
height: auto; /* This can be omitted */
}
This is very useful which you can change the scale of the image by changing just one number (i.e. the factor). No worries about the image height, it will be set automatically by the browser, even if you omit setting image height to auto (because auto is the default value of width and height; see this and this). Also, you can set height instead of setting width.
It would be useful for responsive design in a way, and I think it's really tricky!
Note: It doesn't work with flex-boxes, if you set the parent's width or height. In this case, you must explicitly set width and height, or there would be another tricks for that; flex-boxes works completely different.
The most complex stuffs are hidden in the simplest!
What they mean to me by this sentence is to «take care of scalability».
And for that, there is nothing better than svg. We need to think about caching. Just avoid to embed them directly. Let the client browser do the caching by using external src.
As soon as this is well done, we can scale it at moon size, keeping it smooth...
Svg can be generated server side, but it's always better to carefully create them with (gimp). Then open the svg with a text editor.
If using images, currently, for nice rendering in different screen resolution, the best should be to use a set of them in different resolution, with srcset. Finally just like other dynamics elements, let the client browser do the choice by providing him many options. Again, with caching in mind. it will download and keep only the one it need.
If an html solution exist, we should use it before trying to alter it (by js or css).
https://css-tricks.com/responsive-images-css/
A new css property is in town: image-rendering.
https://drafts.csswg.org/css-images-3/#the-image-rendering
As usual, the most complete answer are the specs!
https://drafts.csswg.org/css-images-3
I have a <canvas> in a div and in order to keep its bounds equal to the div I'm using the following code (I'm creating some of my html/css using Javascript for unrelated reasons, I assume doing in JS should be equivalent).
when I create and add the canvas:
this.canvas = document.createElement("canvas")
this.canvasContainerDiv.appendChild(this.canvas)
this.canvas.style.backgroundColor = "orange"
this.canvas.style.width = "100%"
this.canvas.style.height = "100%"
this.canvas.width = this.canvasContainerDiv.clientWidth
this.canvas.height = this.canvasContainerDiv.clientHeight
Then in the window resize callback:
window.onresize = (e) => {
this.canvas.width = this.canvasContainerDiv.clientHeight
this.canvas.height = this.canvasContainerDiv.clientHeight
}
Unfortunately, the canvas doesn't quite fill the bounds of the parent div. It leaves a few pixels of missing height. So that if I resize the window such that the parent div is 522, the canvas' clientHeight will be 518 or something. In addition, as I resize the window's height, the canvas will grow in height monotonically.
I have many questions about this. 1) is assigning the parent div's clientHeight to the canvas' height property the right way to keep the canvas' height matching it? 2), can I size the canvas' element to its parent div with css width/height alone? 3) why does the canvas grow and grow when I resize the window? 4) why does the canvas' clientHeight not its height (although that is wrong too) wind up coming out slightly smaller than the parent div's clientHeight? the width's match fine?
Some extra information. If I replace the canvas with a div element, I don't see either of the problems I mentioned. The div now spans the exact height of its parent, and doesn't suffer from that infinite height growth issue. This leads me to believe that the sizing issues are related to the canvas' own functionalities like context/drawing size/height properties, etc..
I've run into this before. Try adding the following styles:
html, body { padding: 0; margin: 0; width: 100%; height: 100%; }
I think this has helped me before as well:
body { overflow: hidden; }
If those don't do the trick, there are all kinds of other weird things that can cause undesirable/unexplainable space, sometimes setting line-height: 0, font-size: 0 helps
I just remembered something else that might work without any of the above. Make your canvas { position: absolute; top: 0; left: 0 }
I want to know how, within say a div element, I can load an image from an API (where the user may post whatever size image they want) and I can then format that image to meet the following requirements with CSS.
1) the original dimensions are maintained
2) given a max width and max height that I specify, the attribute of the image out of my control that differs greatest from the corresponding attribute in my specification will be shrunk down to match it, with the other attribute shrinking to maintain dimension. If the image is in fact smaller on both dimensions than my specifications than the dimension of the image that differs the LEAST from its corresponding specification should grow to match it, with the other one again moving to maintain dimension.
3) The image should center itself in it's parent node depending on whether it is the width or the height specification that is not maxed out (ie: if max_width was 200px and images width was only 180 then it should center itself along the x axis).
I suppose I could do it with JS, but I imagine its a common enough desire that there is an easier way to do it than logically inputting it. (Also how with JS can I get access to the height and width attributes of the user uploaded images coming in from the JSON file, and how in JS can I center an image within it's parent node?):
JS/Pseudo code mix looks like this, but I want to know how to do it in CSS if possible:
var body = document.getElementsByTagName('body')[0];
$(document).ready(function(){
var maxHeight = whateEverWeSpecify;
var maxWidth = whateverWeSpecify;
$.getJSON('jsonFileWithPropertyImagePointingToUserUploadedImage', formatImg);
});
function formatImg(json){
var div document.createElement('div');
var img = document.createElement('img');
img.setAttribute('src', json.image);
**//this is the part I dont know how to read dimensions of** incoming image so ill use img.height and width.height to refer to dimensions of incoming JSON img here
var heightDifference = img.height - maxHeight;
var widthDifference = img.width - maxWidth;
if (widthDifference < 0 && heightDifference < 0){
if (heightDifference < widthDifference){
img.style.height = img.height + heightDifference;
}
else{
img.style.width = img.width + widthDifference;
}
}
else{
if (heightDifference > widthDifference){
img.style.height = img.height - heightDifference
}
else{
img.style.width = img.width - widthDifference
}
/*sizes are adjusted to fit specifications at this point. I have no idea
how to center the image along the y or x axis depending on which one is
required other than that we can determine which to change based on which
attributes difference from the specification is greater than 0 (in
essence, which attribute was updated), so if you could help me with this
too for future reference I would greatly appreciate it */
}
}
Try setting a div with the 'background-image' property, and use 'background-size:contain'.
I had this same kind of problem when trying to handle profile pictures in a dating website I was building. You never know if the user is gonna upload a tall or wide picture.
HTML:
<div class="outer">
<div class="image_container"></div>
</div>
CSS:
.outer {
background:lightgray;
width:800px;
height:500px;
}
.image_container {
position:relative;
height:80%;
width:80%;
left:10%;
top:10%;
background-image:url('http://icatcare.org/sites/default/files/kcfinder/images/images/aggressive-cat_0.jpg');
background-repeat:no-repeat;
background-size:contain;
background-color:darkgray;
background-position:center;
}
Here's the codepen. Un-comment the second background-image line to see how this works with a tall image: centered background image.
I put a darker grey background on the inner div just to show you that the div stays at the proportions you set, the CSS handles images of varying sizes within. This solution at least keeps your layout from floating or wrapping oddly based on changing image sizes.
Don't use an <img> tag. Instead, use the background-image property in CSS. This gives you access to background-size:contain;, which will do exactly what you're talking about.
https://developer.mozilla.org/en-US/docs/Web/CSS/background-size
For example:
<style>
.userpic{
width:400px;
height:400px;
background-size:contain;
background-position:center center;
background-repeat:no-repeat;
}
</style>
<div class="userpic" style="background-image:url(myimage.jpg)"></div>
Maybe you will not need this anymore but... to get the original dimensions of the image:
var imgWidth = img.naturalWidth;
var imgHeight = img.naturalHeight;
The answers before me pretty much solved the adjusting image problem with css... except for the code to put the image into the container's background via javascript:
function formatImg(json){
var node = document.getElementById('yourNodeId'); // This is the node where you want to put the image (a div maybe, or the document's body)
var div = document.createElement('div'); // This will be the image's container
div.classList.add('image_container');
div.style.backgroundImage = "url(json.image)";
node.appendChild(div);
}
Just add the styles mentioned before me and problem solved
I'm working on a website that fits within a specific width and height (an 885x610 div with a 1px border and 3px top margin). I would like the user to never have to scroll or zoom in order to see the entire div; it should always be fully visible. Since devices have a wide variety of resolutions and aspect ratios, the idea that came to mind was to set the "viewport" meta tag dynamically with JavaScript. This way, the div will always be the same dimensions, different devices will have to be zoomed differently in order to fit the entire div in their viewport. I tried out my idea and got some strange results.
The following code works on the first page load (tested in Chrome 32.0.1700.99 on Android 4.4.0), but as I refresh, the zoom level changes around. Also, if I comment out the alert, it doesn't work even on the first page load.
Fiddle
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, height=device-height, initial-scale=1.0">
<script type="text/javascript">
function getViewportWidth() {
if (window.innerWidth) {
return window.innerWidth;
}
else if (document.body && document.body.offsetWidth) {
return document.body.offsetWidth;
}
else {
return 0;
}
}
function getViewportHeight() {
if (window.innerHeight) {
return window.innerHeight;
}
else if (document.body && document.body.offsetHeight) {
return document.body.offsetHeight;
}
else {
return 0;
}
}
if (/Android|webOS|iPhone|iPad|iPod|BlackBerry/i.test(navigator.userAgent)) {
var actual_width = getViewportWidth();
var actual_height = getViewportHeight();
var min_width = 887;
var min_height = 615;
var ratio = Math.min(actual_width / min_width, actual_height / min_height);
if (ratio < 1) {
document.querySelector('meta[name="viewport"]').setAttribute('content', 'initial-scale=' + ratio + ', maximum-scale=' + ratio + ', minimum-scale=' + ratio + ', user-scalable=yes, width=' + actual_width);
}
}
alert(document.querySelector('meta[name="viewport"]').getAttribute('content'));
</script>
<title>Test</title>
<style>
body {
margin: 0;
}
div {
margin: 3px auto 0;
width: 885px;
height: 610px;
border: 1px solid #f00;
background-color: #fdd;
}
</style>
</head>
<body>
<div>
This div is 885x610 (ratio is in between 4:3 and 16:10) with a 1px border and 3px top margin, making a total of 887x615.
</div>
</body>
</html>
What can I do to have this website scale to fit both the width and the height?
It's possible to get a consistent behavior. But it's unfortunately very complex. I am working on a script that detects spoofed agents and dynamically rescale the viewport to desktop or other spoofed agents accordingly. I was also facing the zooming issue with Android/Chrome as well as the iOS emulator...
To get around it, you need to disable zooming and/or set the viewport twice. On the first pass, preferably inline in the <head> as you do now, you set your scale and disable user-scaling temporarily to prevent the zoom issue, using the same fixed value for all 3 scales like:
document.querySelector('meta[name=viewport]').setAttribute('content', 'width='+width+',minimum-scale='+scale+',maximum-scale='+scale+',initial-scale='+scale);
Then to restore zooming you set the viewport again on DOMContentLoaded, with the same scale, except that this time you set normal min/max scale values to restore user-scaling:
document.querySelector('meta[name=viewport]').setAttribute('content', 'width='+width+',minimum-scale=0,maximum-scale=10');
In your context, because the layout is fixed and larger than the viewport, initial-scale='+scale is perhaps needed for a more sound alternative for DOMContentLoaded:
document.querySelector('meta[name=viewport]').setAttribute('content', 'width='+width+',minimum-scale=0,maximum-scale=10,initial-scale='+scale);
That should get the viewport to rescale as you would like in Webkit browsers without zooming problems. I say only in webkit because sadly IE and Firefox do not support changing the viewport as per this Browser Compatibility Table for Viewports, Dimensions and Device Pixel Ratios shows: http://www.quirksmode.org/mobile/tableViewport.html
IE has its own way to change the viewport dynamically which is actually needed for IE snap modes to be responsive.
http://timkadlec.com/2012/10/ie10-snap-mode-and-responsive-design/
So for IEMobile and IE SnapMode (IE10&11) you need to dynamically insert an inline <style> in the <head> with something like.
<script>
var s = document.createElement('style');
s.appendChild(document.createTextNode('#-ms-viewport{width:'+width+'px')+';}'));
document.head.appendChild(s);
</script>
And unfortunately, Firefox has neither: The viewport is set for once and for all as the above compatibility table shows. At the moment, for lack of other methods, using CSS Transform (as #imcg pointed out) is the only way to alter the viewport in FireFox Mobile on Android or Gecko OS. I have just tested it and it works in the context of a fixed size design. (In "Responsive Design context", the layout can be rescaled larger via CSS Transform, say at desktop size on a phone, but Firefox still read the phone size MQs. So that's something to be mindful off in RWD context. /aside from webkit)
Though, I have noticed some random Webkit crashes with CSSTransform on Android so I would recommend the viewport method for Safari/Chrome/Opera as more reliable one.
In addition, in order to get cross-browser reliability for the viewport width, you also have to face/fix the overall inconsistency between browsers for innerWidth (note that documentElement.clientWidth is much more reliable to get the accurate layout pixel width over innerWidth) and you also have to deal with devicePixelRatio discrepancies as indicated on the quirksmode.org table.
Update: Actually after some more thought into a solution for my own problem with Firefox, I just found out a way to override the viewport in Firefox Mobile, using document.write(), applied just once:
document.write('<meta name="viewport" content="width='+width+'px,initial-scale='+scale+'">');
Just tested this successfully in both Webkit and Firefox with no zooming issues. I can't test on Window Phones, so I am not sure itf document.write works for IEMobile...
I know this is two years late, but I spent a lot of time working on this problem, and would like to share my solution. I found the original question to be very helpful, so I feel that posting this answer is my way of giving back. My solution works on an iPhone6 and a 7" Galaxy Tab. I don't know how it fares on other devices, but I'm guessing it should mostly behave.
I separated the viewport logic into an external file so that it is easy to
reuse. First, here's how you would use the code:
<HTML>
<HEAD>
<SCRIPT type="text/javascript" src="AutoViewport.js"></SCRIPT>
</HEAD>
<BODY>
<SCRIPT>
AutoViewport.setDimensions(yourNeededWidth, yourNeededHeight);
</SCRIPT>
<!-- The rest of your HTML goes here -->
</BODY>
</HTML>
In actuality, I padded my desired width and height by a slight amount (15 pixels or so) so that my intended display was framed nicely. Also please note from the usage code that you do not have to specify a viewport tag in your HTML. My library will automatically create one for you if one does not already exist.
Here is AutoViewport.js:
/** Steven Yang, July 2016
Based on http://stackoverflow.com/questions/21419404/setting-the-viewport-to-scale-to-fit-both-width-and-height , this Javascript code allows you to
cause the viewport to auto-adjust based on a desired pixel width and height
that must be visible on the screen.
This code has been tested on an iPhone6 and a 7" Samsung Galaxy Tab.
In my case, I have a game with the exact dimensions of 990 x 660. This
script allows me to make the game render within the screen, regardless
of whether you are in landscape or portrait mode, and it works even
when you hit refresh or rotate your device.
Please use this code freely. Credit is appreciated, but not required!
*/
function AutoViewport() {}
AutoViewport.setDimensions = function(requiredWidth, requiredHeight) {
/* Conditionally adds a default viewport tag if it does not already exist. */
var insertViewport = function () {
// do not create if viewport tag already exists
if (document.querySelector('meta[name="viewport"]'))
return;
var viewPortTag=document.createElement('meta');
viewPortTag.id="viewport";
viewPortTag.name = "viewport";
viewPortTag.content = "width=max-device-width, height=max-device-height,initial-scale=1.0";
document.getElementsByTagName('head')[0].appendChild(viewPortTag);
};
var isPortraitOrientation = function() {
switch(window.orientation) {
case -90:
case 90:
return false;
}
return true;
};
var getDisplayWidth = function() {
if (/iPhone|iPad|iPod/i.test(navigator.userAgent)) {
if (isPortraitOrientation())
return screen.width;
else
return screen.height;
}
return screen.width;
}
var getDisplayHeight = function() {
if (/iPhone|iPad|iPod/i.test(navigator.userAgent)) {
if (isPortraitOrientation())
return screen.height;
else
return screen.width;
}
// I subtract 180 here to compensate for the address bar. This is imperfect, but seems to work for my Android tablet using Chrome.
return screen.height - 180;
}
var adjustViewport = function(requiredWidth, requiredHeight) {
if (/Android|webOS|iPhone|iPad|iPod|BlackBerry/i.test(navigator.userAgent)){
var actual_height = getDisplayHeight();
var actual_width = getDisplayWidth();
var min_width = requiredWidth;
var min_height = requiredHeight;
var ratio = Math.min(actual_width / min_width, actual_height / min_height);
document.querySelector('meta[name="viewport"]').setAttribute('content', 'initial-scale=' + ratio + ', maximum-scale=' + ratio + ', minimum-scale=' + ratio + ', user-scalable=yes, width=' + actual_width);
}
};
insertViewport();
adjustViewport(requiredWidth, requiredHeight);
window.addEventListener('orientationchange', function() {
adjustViewport(requiredWidth, requiredHeight);
});
};
If you compare my code closely with the original code found in the question, you will notice a few differences. For example, I never rely on the viewport width or height. Instead, I rely on the screen object. This is important because as you refresh your page or rotate your screen, the viewport width and height can change, but screen.width and screen.height never change. The next thing you will notice is that I don't do the check for (ratio<1). When refreshing or rotating the screen, that check was causing inconsistency, so I removed it. Also, I included a handler for screen rotation.
Finally, I'd just like to say thank you to the person who created this question for laying the groundwork, which saved me time!
If you can't get consistent behavior across devices by changing the viewport meta tag, it's possible to zoom without changing the dimensions using CSS3 transforms:
if (ratio < 1) {
var box = document.getElementsByTagName('div')[0];
box.style.webkitTransform = 'scale('+ratio+')';
box.style.webkitTransformOrigin = '0 0';
}
console.log(box.offsetWidth); // always original width
console.log(box.getBoundingClientRect().width); // new width with scaling applied
Note I've omitted any vendor prefixes other than webkit here in order to keep it simple.
To center the scaled div you could use the translate tranforms:
var x = (actual_width - min_width * ratio) / 2;
var y = (actual_height - min_height * ratio) / 2;
box.style.webkitTransform = 'translateX('+x+'px) translateY('+y+'px) scale('+ratio+')';
box.style.webkitTransformOrigin = '0 0';
Replace your viewport with this :
<META NAME="viewport" CONTENT="width=device-width, height=device-height, initial-scale=1, user-scalable=no"/>
The user-scalable=0 here shall do the job for you.
This shall work for you.If it still doesn't work for we will have to extend the viewport so replace your viewport and add this:
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, target-densitydpi=medium-dpi, user-scalable=0" />
For your javascript error have a look at this link:
scale fit mobile web content using viewport meta tag
Set the height and width of the div's parent elements (html and body) to 100% and zero out the margin.
html, body {
margin: 0;
height: 100%;
width: 100%;
}
Next, because you want a border on the div you need to make sure that the border width is included when you specify the width / height of the element, to do this use box-sizing: border-box on the div.
Because you want a 3px top margin on the div relative positioning of the div will result in a height that is 3px too tall. To fix this use absolute positioning on the div and set top, left, and bottom to 0.
div {
position: absolute;
top: 3px;
left: 0;
bottom: 0;
box-sizing: border-box;
width: 100%;
border: 1px solid #f00;
background-color: #fdd;
}
Here's a working example.
UPDATE: I think I misunderstood the question. Here's a sample code that adjusts the "zoom" depending on the device's viewport
http://jpanagsagan.com/viewport/index2.html
Take note that I used jquery to append the meta tag as I am having issue using the vanilla append.
One thing I noticed is that if you hard-coded in the HTML and change it via JS, the document won't apply the correction (needs verification). I was able to change it via JS if there is no previous tag in the HTML, thus I used append.
You may play around with the ratio, but in the example I used width of the viewport divided by width of the div.
hope this helps.
UPDATE: I think I misunderstood the question. Here's a sample code that adjusts the "zoom" depending on the device's viewport
http://jpanagsagan.com/viewport/index2.html
Take note that I used jquery to append the meta tag as I am having issue using the vanilla append.
One thing I noticed is that if you hard-coded in the HTML and change it via JS, the document won't apply the correction (needs verification). I was able to change it via JS if there is no previous tag in the HTML, thus I used append.
You may play around with the ratio, but in the example I used width of the viewport divided by width of the div.
hope this helps.
I think Steven's should be the accepted answer.
In case it is helpful someone else, I would add the following 2 things to Steven's AutoViewport.js, in order to center the view within the viewport when the user is in landscape view:
Add "var viewport_margin = 0;" as the first line of code (as it's own line before "function AutoViewport() {}".
Add "viewport_margin = Math.abs(actual_width-(ratio*requiredWidth))/(actual_width*2)*100;" after the line that reads "document.querySelector('meta[name="viewport"]').setAttribute('content', 'initial-scale=' + ratio + ', maximum-scale=' + ratio + ', minimum-scale=' + ratio + ', user-scalable=yes, width=' + actual_width);"
Thanks for all those who posted to bring this solution to light.
UPDATE: In Android, my additions only appear to work with API 24+. Not sure why they aren't working with APIs 19-23. Any ideas?