I would like to give users the ability to increase/decrease the rendering size of the content inside of a web app.
The CTRL+ and CTRL- features (or CTRL1 through CTRL9) of Chrome & Firefox are handy - but is there a way from JavaScript to execute those features?
Clarification:
I want the user to be able to achieve this via mouse-clicks, not keypresses, so something like this:
function resize_rendering() {
// code that executes ctrl+ or crtl-
}
The browser zoom level is a user setting, which is there for accessiblity purposes; it's not intended for the site developer to ever know or care what the zoom level is, so I would expect that you'll have trouble doing exactly what you want. Certainly, it'll be hard to get it right in a way that works cross-browser.
The normal approach to this is to have a sizing gadget that changes the base font size.
If all the font sizes in your site are in em units, then you can change the sizes of all the text on the site with a single CSS change.
So you would have a set of buttons on the site which use Javascript to set the font-size of the <body> element to (for example) 12, 14, 16 or 18 pixels, depending on the element clicked.
There's a write-up of the technique here: http://labnol.blogspot.com/2006/12/allow-site-visitors-to-change-font.html
You are not allowed to by design. You can't change a user's browser setting via Javascript.
You can do other things, like modify all of the CSS on your page to scale everything down to simulate a CTRL-, but that's all.
In some browsers you can capture CTRL+/- before the browser does, allowing you to stop those events from occuring. But you cannot do the oppisite - you cannot cause those events to occur from your own script.
This library here by John Resig is a jQuery plugin that should do the job. There's some samples. Its quite easy to hookup combinations of keys as well.
You could intercept the keystroke in a general key event handler (such as on the window object), check to see if it's the one you're looking for, and if so, call stopPropagation() on the event parameter (and return false) to prevent the browser from then handling it on its own.
You'd then have to perform the sizing operation yourself, such as by modifying a stylesheet using JavaScript.
window.addEventListener( 'keydown', function( e ) {
if ( /* check e.keyCode etc. */ ) {
// modify global style to increase/decrease font size
e.stopPropagation();
return false;
} );
Look towards CSS property "zoom"
/* ctrl++ */
body {
zoom: 1.1;
}
For JS, it's like:
function resize_rendering(zoom) {
let currentZoom = parseFloat(document.body.style.zoom) || 1
document.body.style.zoom = currentZoom * zoom
}
resize_rendering(1.1) // ctrl++
resize_rendering(0.9) // ctrl+-
Related
I'm writing a website with a canvas in it. The website has a script that runs successfully on every refresh except for a line at the end. When the script ends with:
document.body.onresize = function() {viewport.resizeCanvas()}
"document.body.onresize" is unchanged. (I double-checked in Chrome's javascript console: Entering "document.body.onresize" returns "undefined".)
However, when the script ends with:
document.body.onresize = function() {viewport.resizeCanvas()}
console.log(document.body.onresize)
"document.body.onresize" does change. The function works exactly as it should.
I can't explain why these two functionally identical pieces of code have different results. Can anyone help?
Edit: As far as I can tell, "document.body" is referring to the correct "document.body". When I call console.log(document.body) just before I assign document.body.onresize, the correct HTML is printed.
Edit 2: A solution (sort of)
When I substituted "window" for "document" the viewport's "resizeCanvas" function was called without fail every time I resized the window.
Why does "window" work while "document" only works if you call "console.log" first? Not a clue.
Resize events: no go
Most browsers don't support resize events on anything other than the window object. According to this page, only Opera supported detecting resizing documents. You can use the test page to quickly test it in multiple browsers. Another source that mentions a resize event on the body element specifically also notes that it doesn't work. If we look at these bug reports for Internet Explorer, we find out that having a resize event fire on arbitrary elements was an Internet Explorer-only feature, since removed.
Object.observe: maybe in the future
A more general method of figuring out changes to properties has been proposed and will most likely be implemented cross-browser: Object.observe(). You can observe any property for changes and run a function when that happens. This way, you can observe the element and when any property changes, such as clientWidth or clientHeight, you will get notified. It currently works only in Chrome with the experimental Javascript flag turned on. Plus, it is buggy. I could only get Chrome to notify me about properties that were changed inside Javascript, not properties that were changed by the browser. Experimental stuff; may or may not work in the future.
Current solution
Currently, you will have to do dirty checking: assign the value of the property that you want to watch to a variable and then check whether it has changed every 100 ms. For example, if you have the following HTML on a page:
<span id="editableSpan" contentEditable>Change me!</span>
And this script:
window.onload = function() {
function watch(obj, prop, func) {
var oldVal = null;
setInterval(function() {
var newVal = obj[prop];
if(oldVal != newVal) {
var oldValArg = oldVal;
oldVal = newVal;
func(newVal, oldValArg);
}
}, 100);
}
var span = document.querySelector('#editableSpan');
// add a watch on the offsetWidth property of the span element
watch(span, "offsetWidth", function(newVal, oldVal) {
console.log("width changed", oldVal, newVal);
});
}
This works similarly to Object.observe and for example the watch function in the AngularJS framework. It's not perfect, because with many such checks you will have a lot of code running every 100 ms. Additionally any action will be delayed 100 ms. You could possibly improve on this by using requestAnimationFrame instead of setInterval. That way, an update will be noticed whenever the browser redraws your webpage.
What you can do is that if you know for certain what particular action triggers a resize on your element that doesn't resize the full window you can trigger a resize event so your browser recalculate all of the divs (if by the case the browser is not triggering the event correctly).
With Jquery:
$(window).trigger('resize');
In the other hand, if you have an action that resizes an element you can always hold from that action to handle other following logic.
<script>
function body_OnResize() {
alert('resize');
}
</script>
<body onresize="body_OnResize()"></body>
We are trying to build an HTML game in which abobe edge is being used for animations and those animations are being inserted into iframes. We are trying to preload the iframes before removing the 'loading screen' so that the users won't see blank iframes initially.
Here is the code for loading the iframes.
We have a global variable var middleBotLoaded = false;
The following function tries to dynamically populate the iframe and once the iframe has loaded , we are assigning the variable to true
function _trackIFrameLoading()
{
if (document.getElementById("botzmiddleidlesequence_iframe").attachEvent)
{
document.getElementById("botzmiddleidlesequence_iframe").attachEvent("onload", function() { middleBotLoaded = true; });
}
else
{
document.getElementById("botzmiddleidlesequence_iframe").onload = function() { middleBotLoaded = true; };
}
document.getElementById("botzmiddleidlesequence_iframe").src = APP_BASE_URL + "blitzbotzidlesequence/blitzbotz/"+middleBotzId;
}
We have a method to check if the global variable has become true and if so , we are removing the loading screen.The method is being called in a interval of 500 milliseconds
setTimeout(_haveAllCharactersLoaded,500);
function _haveAllCharactersLoaded()
{
if(middleBotLoaded == true)
{
$(jOverlay).fadeOut(800, function() {
$(jOverlay).remove();
});
}
else
{
setTimeout(_haveAllCharactersLoaded,500);
}
}
The problem is that even after the loading screen disappears , the iframe contents take time to come up on the screen .
We have observed that the duration depends on the speed of the net connection , but then , isn't using onload the whole point of making sure that the contents have loaded.
Is there any other approach for dealing with this problem.
Thanks.
EDIT : I have to wait for two days before I can start a bounty but I am willing to award it to anyone who can provide a canonical answer to the question.
I have two answers here.
First, I think you should reconsider the way you're coding this game, unless it's a static, turn based game that only relies upon animations (think Pokemon.)
Second, I have a suggestion for you to try in fixing your code.
First Answer:
You asked if there is any other approach to dealing with this problem.
My first reaction to that, would be to skip using iFrames entirely. Adobe Edge may provide you with a good way to craft animations, but for use in a game engine you will only find yourself fighting against the design of how Adobe Edge handles it's animations.
Instead, I would recommend learning how to use HTML5's canvas element. Canvas is built to handle dynamically loaded content (such as your game engine will be generating.) I can only imagine the event of having particle effect animation overlayed onto a game character as he is hit by a weapon. With your current approach, would you place that in an iFrame? Then, how would you ensure that this particle effect is placed on the correct location on the object?
There are many resources out there to help you begin learning the code you need to make a true game engine in the browser. I would recommend beginning by learning how Canvas works. If you want to animate using the DOM, learn about requestAnimationFrame.
http://creativejs.com/
http://www.paulirish.com/2011/requestanimationframe-for-smart-animating/
https://developer.mozilla.org/en-US/docs/Web/Guide/HTML/Canvas_tutorial
Second Answer:
I would recommend looking into the variable scope of your middleBotLoaded. This answer (Set variable in parent window from iframe) would be a good place to look.
Instead of using document.getElementById("botzmiddleidlesequence_iframe").attachEvent("onload", function() { middleBotLoaded = true; });
try using document.getElementById("botzmiddleidlesequence_iframe").attachEvent("onload", function() { parent.middleBotLoaded = true; });
Alternatively, try something along these lines:
onLoad event:
document.getElementById("botzmiddleidlesequence_iframe").attachEvent("onload", function() { parent.middleBotLoaded();});
Function to handle loading:
function middleBotLoaded()
{
$(jOverlay).fadeOut(800, function() {
$(jOverlay).remove();
});
}
It's a better practice to directly call an event, rather than polling for variable changes using setTimeout.
www.warhawkcomputers.com/Birenbaum
This site has various projects for my Computer class that I am in. A check is coming up and all programs will need to work in FF and IE.
My Bouncing Ball, Race Track, and Tanks projects under Third quarter as well as pong under Fourth Quarter work in IE when the objects need to be moved by a continuously adding variable performed in a javascript script, and it works perfectly fine in IE, but when viewed with Firefox 3, the moving objects no longer move and I have tested to find out it gets the variables but seems to only add it once and that the document.getElementById("objectname").style.left = "continuously adding variable" seems to not be executed despite being in a timer running every 10 milliseconds.
Also, none of my keypress code works in Firefox, but I believe that is because I use an outdated method of moving objects via keypress. This is largely due to my teacher's outdated methods.
Thanks for all of your guys's help.
You need to add a 'units' to your positions:
document.getElementById("ball").style.left = x + 'px';
document.getElementById("ball").style.top = y + 'px';
That will work in FF as well now.
Firefox does not use a global event object. They pass an event object into the handler. As a result, you need to modify your Move function:
function Move(e) {
/* snip */
var whichkey = window.event ? window.event.keyCode : e.keyCode;
/* ... */
Gerrat is absolutely correct about the other problem you asked about.
EDIT: this won't work with how you hooked your event handler in the body tag. You need to remove the onkeydown="Move()" attribute from the body tag and add the following code at the top of JavaScript.js:
document.body.onkeydown=Move;
If allowed to do so by your teacher, you would be MUCH better off using jQuery or some other framework.
I know that IE supports the onresize event for any element while FireFox supports it for the window object only.
Since I need that functionality for any modern browser I am currently using a (fairly fast) interval timer that continuously checks the clientWidth/clientHeight of an element to emulate the onresize. Of course this is very inefficient and I'd like to know if there is a cross-browser way that does this. Or, alternatively, different methods that work in Firefox/WebKit/IE so that the timer-method can be used as a fallback for remaining browsers.
Targeting at plain JavaScript, instead of jQuery / Prototype etc.
If you don't want to use jQuery then you must check what browser sends to your's script, so:
if(!element.onresize != null) element.onresize += event;
if(!element.onresizeAnotherHandler != null) element.onresizeAnotherHandler += event;
I don't know is it that easy, but it is a general concept.
EDIT: It looks like there is not event handler for Firefox. But there is an trick:
If you are changing width in your's JS just create an function update
function update(var nx, var ny){
// do something with nx and ny
}
If you added an resize handle using CSS do it using JavaScript
If you are using something like 80% in your's CSS then handle window.onresize:
function resize(){
alert(document.getElementById("test").style.width);
}
window.onresize = resize;
It looks that there is no other way.
One of the most difficult problems in my javascript experience has been the correct (that is "cross-browser") computing of a iframe height.
In my applications I have a lot of dynamically generated iframe and I want them all do a sort of autoresize at the end of the load event to adjust their height and width.
In the case of height computing my best solution is the following (with the help of jQuery):
function getDocumentHeight(doc) {
var mdoc = doc || document;
if (mdoc.compatMode=='CSS1Compat') {
return mdoc.body.offsetHeight;
}
else {
if ($.browser.msie)
return mdoc.body.scrollHeight;
else
return Math.max($(mdoc).height(), $(mdoc.body).height());
}
}
I searched the internet without success. I also tested Yahoo library that has some methods for document and viewport dimensions, but it's not satisfactory.
My solution works decently, but sometimes it calculates a taller height.
I've studied and tested tons of properties regarding document height in Firefox/IE/Safari: documentElement.clientHeight, documentElement.offsetHeight, documentElement.scrollHeight, body.offsetHeight, body.scrollHeight, ...
Also jQuery doesn't have a coherent behavior in various browser with the calls $(document.body).height(), $('html', doc).height(), $(window).height()
I call the above function not only at the end of load event, but also in the case of dynamically inserted DOM elements or elements hidden or shown. This is a case that sometimes breaks the code that works only in the load event.
Does someone have a real cross-browser (at least Firefox/IE/Safari) solution? Some tips or hints?
Although I like your solution, I've always found IFRAMEs to be more trouble than they're worth.
Why ? 1. The sizing issue. 2. the iframe has that src attribute to worry about. i.e. absolute path. 3. the extra complexity with the pages.
My solution - DIVs which are dynamically loaded through AJAX calls. DIVs will auto size. Although the AJAX code requires javascript work (which can be done through frameworks) they are relative to where the site is. 3 is a wash, you're trading complexity in pages up to javascript.
Instead of <IFRAME ...> use <DIV id="mystuff" />
Do the ajax call to fill the mystuff div with data and let the browser worry about it.
This has been without an accepted answer for awhile, so I wanted to contribute the solution I ended up going with after some research. This is cross-browser and cross-domain (e.g. when the iframe points to content from a different domain)
I ended up using html5's message passing mechanism wrapped in a jQuery pluging that makes it compatible with older browsers using various methods (some of them described in this thread).
The end solution is very simple.
On the host (parent) page:
// executes when a message is received from the iframe, to adjust
// the iframe's height
$.receiveMessage(
function( event ){
$( 'my_iframe' ).css({
height: event.data
});
});
// Please note this function could also verify event.origin and other security-related checks.
On the iframe page:
$(function(){
// Sends a message to the parent window to tell it the height of the
// iframe's body
var target = parent.postMessage ? parent : (parent.document.postMessage ? parent.document : undefined);
$.postMessage(
$('body').outerHeight( true ) + 'px',
'*',
target
);
});
I've tested this on Chrome 13+, Firefox 3.6+, IE7, 8 and 9 on XP and W7, safari on OSX and W7. ;)
Since in your example you're accessing the document inside the IFRAME I guess you're talking about knowing the height of the document and not of the frame itself. Also, that means that the content comes from your own website and you have control over it's contents.
So, why don't you simply place a DIV around your content and then use the clientHeight of that DIV?
Code you load in your IFRAME:
...
<body>
<div id="content">
...
</div>
</body>
The parent document:
function getDocumentHeight(mdoc) {
return mdoc.getElementById("content").clientHeight;
}
BTW, this part of your example function does not make sense as "document" does not refer to the IFRAME:
var mdoc = doc || document;
Here is a solution that seems to work. Basically, the scrollHeight is the correct value in most cases. However, in IE (specifically 6 and 7), if the content is simply contained in text nodes, the height is not calculated and just defaults to the height set in CSS or on the "height" attribute on the iframe. This was found through trial and error, so take it for what it is worth.
function getDocumentHeight(doc) {
var mdoc = doc || document;
var docHeight = mdoc.body.scrollHeight;
if ($.browser.msie) {
// IE 6/7 don't report body height correctly.
// Instead, insert a temporary div containing the contents.
var child = $("<div>" + mdoc.body.innerHTML + "</div>", mdoc);
$("body", mdoc).prepend(child);
docHeight = child.height();
child.remove();
}
return docHeight;
}
I have found this solution to work in ie 6+, ff 3.5+, safari 4+. I am creating and appending an iframe to the document. The following code is executed at the end of jQuery's load event after some other dom manipulation. The timeout is needed for me because of the additional dom manipulation taking place in the load event.
// sizing - slight delay for good scrollheight
setTimeout(function() {
var intContentHeight = objContentDoc.body.scrollHeight;
var $wrap = $("#divContentWrapper", objContentFrameDoc);
var intMaxHeight = getMaxLayeredContentHeight($wrap);
$this.height(intContentHeight > intMaxHeight ? intMaxHeight : intContentHeight);
// animate
fireLayeredContentAnimation($wrap);
}, 100);
I have some sizing constraints to consider, which is what the getMaxLayeredContentHeight call is checking for me. Hope that helps.
Here's a script that resizes the iFrame depending on the body inside it's height.