Friday, August 27, 2010

Detecting browser height

Here is an example of one way to find out the height of the browser window from inside Flex. It is very easy to find this out if your have your Flex application set to height="100%". But if you use a fixed height like height="300" then stage.stageHeight and app.height both return 300.

This example uses the ExternalInterface to determine the height of the browser by calling the eval javascript function (it is a built-in function).

E.g.
var browserHeight:Number = ExternalInterface.call("eval", "window.innerHeight");

Different browsers (NS, FF, Chrome, IE, Safari) obviously don't all support this call. So far the window.innerHeight property works on all of them except IE. For IE you can use this one (IE 7 and above I think?):
var browserHeight:Number = ExternalInterface.call("eval", "document.documentElement.clientHeight");

And if your browser is older you can try this one:
var browserHeight:Number = ExternalInterface.call("eval", "document.getElementsByTagName('body')[0].clientHeight");

Browsers will also handle errors differently, so if you try to use one of those JavaScript functions in one browser you might get undefined return, and in another browser you might get an error dialog box.

Here is an example of this in action, it opens in a new browser window to show it properly.
If it was embedded on this page using an <iframe> tag then the size returned is that of the iframe, not the browser.

- Browser Height Example -
Click this image to open the real application


Obviously if you were interested in the browser width then you could do exactly the same thing but replace innerHeight with innerWidth and clientHeight with clientWidth.

Comments welcome.

Wednesday, August 11, 2010

Synchronized Scrollbars

I recently had a need for two side-by-side TextAreas whose vertical scrollbars were synchronized. So if you drag the left scrollbar, the right one updates, and vice versa.

I've written a utility class called LinkedScrollers that synchronizes the scrolling of two Scrollers. It has 5 public properties:
  • enabled (defaults to true) - set to false to allow scrollbars to move independently
  • scroller1 - the first Scroller
  • scroller2 - the second Scroller
  • component1 - the first SkinnableComponent, e.g. List, TextArea
  • component2 - the second SkinnableComponent (List, TextArea, etc)
Originally I wanted to bind the scrollers directly to my LinkedScrollers in MXML like this:
<spark:LinkedScrollers scroller1="{list.scroller}" scroller2="{textArea.scroller}"/>
But unfortunately the scroller property on List/TextArea is not bindable, so that didn't work. You can still use the scroller1 and scroller2 in ActionScript (e.g. in the application's creationComplete handler) to set the scrollers.

So another solution is to set the component1 and component2 properties to the two SkinnableComponents that you want to link (Lists, TextAreas, etc). It should work for any SkinnableComponent that contains a "scroller" skin part.
E.g.
<spark:LinkedScrollers component1="{list}" component2="{textArea}"/>

Here it is in action, view source enabled (right click on the example below):


As you might guess, this follows on from my previous post on Spark TextArea With Line Numbers.