Browser Height in IE? NOT document height

Discussion in 'Javascript' started by Kepler, Sep 9, 2004.

  1. Kepler

    Kepler Guest

    How do you get the height of the client browser in IE? Both
    document.body.clientHeight and document.body.offsetHeight return the
    height of the document. If the page is long and there's a vertical
    scrollbar, you get the height of the entire document, screwing up any
    chance of centering a window in the browser using these values.

    Is there a way to get the height of the actual browser window and not
    the entire page height?

    Thanks.
     
    Kepler, Sep 9, 2004
    #1
    1. Advertising

  2. Kepler wrote:

    > How do you get the height of the client browser in IE? Both
    > document.body.clientHeight and document.body.offsetHeight return the
    > height of the document.


    Hmm, in modern browsers (including IE) clientHeight does return the
    client height, and scrollHeight returns the whole document height.

    <URL:http://jibbering.com/faq/#FAQ4_9>

    What version of IE, and platform?


    <FAQENTRY>
    In 4_9 the variable redefinition in the different branchs is misleading,
    variable definitions occur once, when execution scopes are created,
    their initialization come later within the corresponding statement
    execution (Ecma-263-12.2).
    </FAQENTRY>
     
    Yann-Erwan Perio, Sep 9, 2004
    #2
    1. Advertising

  3. Yann-Erwan Perio wrote:
    >> How do you get the height of the client browser in IE?
    >> Both document.body.clientHeight and
    >> document.body.offsetHeight return the height of the
    >> document.

    >
    > Hmm, in modern browsers (including IE) clientHeight
    > does return the client height, and scrollHeight
    > returns the whole document height.
    >
    > <URL:http://jibbering.com/faq/#FAQ4_9>
    >
    > What version of IE, and platform?


    I am wondering whether there isn't a mismatch in terminology here, and
    it is the IE equivalents of outerWidth/Height that is wanted by the OP.

    > <F ... Y>
    > In 4_9 the variable redefinition in the different branchs
    > is misleading, variable definitions occur once, when
    > execution scopes are created, their initialization come
    > later within the corresponding statement execution
    > (Ecma-263-12.2).
    > </F ... Y>


    Yes, it is not syntactically incorrect but it is certainly misleading.

    Personally, I don't like the answer to 4.9 much anyway. Preferring the
    innerWidth/Height properties certainly simplifies the code considerably
    but they are not the equivalents of the clientWidth/Height properties on
    the root element in IE browsers as they represent dimensions that
    includes the thickness of the scroll bars (if present), while the
    'client' values return the dimensions inside the scroll bar.

    In most practical circumstances I have found that the dimensions inside
    the scroll bars are the dimensions that I would prefer to be using. If,
    for example, a page has a vertical scroll bar but no horizontal scroll
    bar (the common case in a fluid layout) I don't want to be positioning
    anything behind the vertical scroll bar because that is likely to
    introduce a horizontal scroll bar. And that horizontal scroll bar might
    flicker on an off if I am moving something that might partly slip behind
    the vertical scroll bar. So to control movement, or clip the offending
    elements appropriately, to the maximum extent possible, I need (want) to
    know the dimensions inside the scroll bars and only use
    innerWidth/Height when it is the only mechanism available.

    Unfortunately the very simple rules that apply to determining which
    element should be read to retrieve the viewport/visible client-region
    (inside any scroll bars) dimensions on IE browsers are not really
    applicable to any other DOM browsers (except by coincidence on some).
    You simply cannot apply the - document.compatMode - test and be left
    with a reference to the correct object (Opera is a particular offender
    here). Logic that prefers to use the innerWidth/Height side-steps that
    problem, but preferring to use the correct clientWidth/Height properties
    brings you face to face with it.

    So the problem is that the clientWidth/Height dimensions reported by
    either of - document.body - and/or - document.documentElement - might be
    either the dimensions of the viewport/client-region (that are wanted) or
    they might be the client area of the document (which might be bigger
    than the viewport, or smaller). However, given a browser that also
    supports innerWidth/Height, some logic can be applied towards deciding
    which should be used. For example, the real viewport/client-region
    dimensions will never exceed the corresponding innerWidth/Height
    dimensions (only the document dimensions can do that, though they may
    not), and if a page is scrolled it is reasonable to assume that it has a
    scroll bar, which means that in that case the 'client' dimension inside
    the scroll bar must be smaller than the corresponding 'innner' value.

    The problem with this testing strategy is that you cannot always make a
    final decision as to which object is reporting the true 'client'
    dimensions as the result of one test. A provisional decision can be made
    and its result will be 'safe' (in that they will never be worse than the
    'inner' values). However, conditions might change. A page that was not
    scrolled might be scrolled, or the page might be re-sized, altering the
    'inner' values but not necessarily in the same way as the document
    dimensions. Those changes would allow the provisional decision to be
    reviewed and possibly a final decisions to be made at that point.

    This brings me to the progressively self re-configuring ("Russian doll")
    object/system that I said I would publish a few months back. That object
    was intended to address this particular problem. I still haven't had
    time to fully test it (at least to the extent that I would consider
    necessarily), it hasn't yet been shown to any Mac browsers, and I don't
    think Konqueror has seen this current version yet either. But here it is
    anyway:-


    /* The - getWindowState - function can be regarded a factory that
    returns a simple interface object, though that interface is in
    reality always a reference to the same object (a 'singleton
    class').

    var windowInterface = getWindowState();

    NOTE: the first call to the - getWindowState - function should
    not happen until after the browser has passed the opening BODY
    tag (else the corresponding element will not be available in
    the DOM).

    Code is expected to retrieve a reference to the interface and
    store it locally for re-use.

    The interface has 4 methods that return pixel dimensions as
    numbers (or NaN if the browser does not support any mechanism
    for reading the corresponding information):-

    var clientWidth = windowInterface.getWidth();
    var clientHeight = windowInterface.getHeight();
    var verticalScroll = windowInterface.getScrollY();
    var horizontalScroll = windowInterface.getScrollX();

    Wherever possible non-NaN returned values for width and height
    will be the vewport/client-region dimensions _not_ including
    the scroll bar thicknesses. They will always be equal to, or
    less than the innerWidth/Height values on browsers that support
    those properties.

    */
    function getWindowState(){
    var global = this;
    var lastSt,lastSl,lastIW,lastIH,bodyRank = null,docElRank = null;
    var twoTestCount = 0;
    /* When the system reports dimension and scroll values it is
    reading those values form one of a number of objects,
    depending on which the browser supports, and which seems
    to be providing the best information. When decisions are
    made about which object to read a reference to that abject
    is assigned to one of the following variables. The initial
    object assignments represent defaults and will result in
    the interface returning NaN values of the browser does not
    support any mechanism for reporting the relevant
    information:-
    */
    var readScroll = {scrollLeft:NaN,scrollTop:NaN};
    var readSizeC = {clientWidth:NaN,clientHeight:NaN};
    var readSizeI = {innerWidth:NaN,innerHeight:NaN};
    /* Default property names used when reading values form the
    object assigned to the - readScroll - variable. Their
    values are changed to - pageXOffset - and -pageYOffset -
    respectively whenever the global object is assigned to -
    readScroll -, which happens whenever it supports those
    properties as numeric values:-
    */
    var readScrollX = 'scrollLeft';
    var readScrollY = 'scrollTop';
    /* This object represents the interface to the scrolling and
    viewport/client-region dimensions. Calls to - getWindowState
    - always return a reference to this one object. Its methods
    are dynamically changed during the configuration of this
    system to provide the best information available in the
    shortest time possible:-
    */
    var theInterface = {
    getScrollX:getScrlXMain,
    getScrollY:getScrlYMain,
    getWidth:function(){return initWidthHeight('getWidth');},
    getHeight:function(){return initWidthHeight('getHeight');}
    };
    /* When the system makes a provisional decision about which of
    - global -, - document.body - or - document.documentElement
    - it should be reporting dimensions from, but cannot entirely
    eliminate the other contenders, that provisional decision does
    not need to be reviewed until one of the criteria on which is
    was based is changed (as the code will draw the same conclusion
    otherwise). So these functions are used to monitor for changes
    in the scroll of the browser window and only re-examine the
    provisional decision if the window has changed scroll (as that
    may have altered the decisions making criteria in a way that
    allows a more final conclusion).

    The scroll values actually returned are not influenced by this
    testing, it is only done because it may allow the resolution of
    the viewpot/client-region dimension reporting issue:-
    */
    function getScrlXTest(){hasChangedScroll();return getScrlXMain();}
    function getScrlYTest(){hasChangedScroll();return lastSl;}
    /* The following two functions are the final scroll position
    reporting functions. They read a value from the - readScroll -
    object. That object may be the NaN returning default (if no
    mechanism is available for reporting scroll positions on the
    current browser), It may have been assigned - document.body - or
    - document.documentElement -, or (and whenever - pageYOffset
    - and -pageXOffset - are available as numeric properties) a
    reference to the global object. The property names held in the
    variables - readScrollX - and - readScrollY - will be either
    scrollTop/Left or pageX/YOffset, depending on which object is
    being used to report the values.

    Once the viewport/client-region dimension reporting issues have
    been finalised these two functions are assigned as method of
    the interface. They are also the interface's default methods,
    only swapped for the testing method if the dimension reporting
    cannot be resolved at the first attempt:-
    */
    function getScrlXMain(){return readScroll[readScrollX];}
    function getScrlYMain(){return readScroll[readScrollY];}
    /* These two functions return the innerHeight/Width properties
    from the object referred to by - readSizeI -. That object is
    initially a NaN returning default but is replaced with a
    reference to the global object if that object is found to have
    numeric innerHeight/Width properties. In the event that this
    system decides that the innerHeight/Width properties of the
    global object are the most appropriate these two functions
    will be assigned to the - getWidth - and - getHeight - methods
    of the interface object. Otherwise they are employed in the
    decision making process to compare innerHeight/Width values
    with clientHeight/width values of the object being considered
    as the optimum candidate for returning browser window inner
    dimensions:-
    */
    function getWidthI(){return readSizeI.innerWidth;}
    function getHeightI(){return readSizeI.innerHeight;}
    /* The - readSizeC - variable refers to one of - document.body -
    or - document.documentElement - (or the default), and these
    two method return clientWidth/Height values from that object.
    In the event that either the - body - or - documentElement -
    are settled upon as the best source for the
    viewport/client-region dimensions then these functions are
    assigned to the corresponding methods of the interface:-
    */
    function getWidthC(){return readSizeC.clientWidth;}
    function getHeightC(){return readSizeC.clientHeight;}
    /* When the system makes a provisional decision about which of
    - global -, - document.body - or - document.documentElement
    - it should be reporting dimensions from, but cannot entirely
    eliminate the other contenders, that provisional decision does
    not need to be reviewed until one of the criteria on which is
    was based is changed (as the code will draw the same conclusion
    otherwise). So these functions are used to monitor for changes
    in the size of the browser window and only re-examine the
    provisional decision if the window has changed size (as that may
    have altered the decisions making criteria in a way that allows
    a more final conclusion):-
    */
    function getHeightSmart(){
    return (hasChangedSize()?theInterface.getHeight:getHeightC)();
    }
    function getWidthSmart(){
    return (hasChangedSize()?theInterface.getWidth:getWidthC)();
    }
    /* In the event that both - document.body - or -
    document.documentElement - can be eliminated from consideration
    (or are not available on the browsers (Net 4), the system
    resorts to using the innerHeight/Width functions for the
    interface methods (which may be returning default NaN values if
    they are also unsupported). This may happen in several places
    so the code that does the method swapping is wrapped in a single
    inner function:-
    */
    function setInnerWH(){
    theInterface.getWidth = getWidthI;
    theInterface.getHeight = getHeightI;
    }
    /* Whenever the client-region/viewport dimension reporting system
    is able to make its final decision changes in the scroll position
    of the window are not longer interesting and the interface
    methods
    that return scroll values can be swapped to the simplest and
    quickest versions available. This may happen in several places so
    the code that does the swapping is wrapped in a single inner
    function:-
    */
    function setMinimumScroll(){
    theInterface.getScrollX = getScrlXMain;
    theInterface.getScrollY = getScrlYMain;
    }
    /* The next two functions check for changes in the dimensions of a
    window (as reported by innerHeight/width) or the scroll position
    of the window, and re-execute the viewport/client-region test
    function (as assigned to - threeObjectTest -) if those values
    have changed:-
    */
    function hasChangedSize(){
    if(
    (lastIH != (lastIH = getHeightI()))||
    (lastIW != (lastIW = getWidthI()))
    ){
    threeObjectTest();
    return true;
    }
    return false;
    }
    function hasChangedScroll(){
    if(
    (lastSl != (lastSl = getScrlYMain()))||
    (lastSt != (lastSt = getScrlXMain()))
    ){
    threeObjectTest();
    }
    }
    /* The next function is the one that does the tests that decide
    whether one of - document.body - or - document.documentElement
    - can be eliminated form consideration as candidates for
    reporting the viewport/client-region dimensions, or it returns
    a value that is used to decide with of the two is providing the
    information most likely to be correct.

    The parameters are two objects: testObj - is one of -
    document.body - or document.documentElement - and - rankObj -
    is an object that holds information resulting form this test,
    or previous tests on the same - testObj - object.

    When this function returns NaN the corresponding - testObj -
    is eliminated from consideration as a candidate for
    viewport/client-region dimension reading. Otherwise the object
    with the _lowest_ - rank - value is used provisionally:-
    */
    function rankObj_inner(testObj, rankObj){
    /* Variables that will hold vertical and horizontal differences
    between client and inner values:-
    */
    var dv,dh;
    /* If client dimensions exceed inner dimensions then this
    object cannot be reporting viewport/client-region
    dimensions. So values of either dv or dh that are less
    than zero result in this function returning NaN:-
    */
    if(
    ((dv = (global.innerHeight - testObj.clientHeight)) >= 0)&&
    ((dh = (global.innerWidth - testObj.clientWidth)) >= 0)&&

    /* If the page is scrolled - getScrlX/YMain() - will return
    a non-zero value. A scrolled page will have a
    corresponding scroll bar so a non-zero vertical scroll
    value implies a vertical scroll bar and so the - dh -
    value will be non-zero on an object reporting
    viewport/client-region dimensions. Similarly with
    horizontal scrolling. Objects are eliminated if a
    non-zero scroll value is not accompanied by a non-zero
    difference between inner and client values in the
    perpendicular axis:-
    */
    (!(getScrlXMain()&&!dv))&&
    (!(getScrlYMain()&&!dh))&&
    /* Non-zero values for - dh - and dv - will represent the
    thickness of the scroll bars. While scrollbar dimensions
    are user-configurable and OS dependent, it is unlikely
    that they will change while this script is running. The
    - rankObj - has - hDiff - and - vDiff - properties that
    are assigned non-zero values only once. If this is the
    second+ time that this object has been tested and it has
    non-zero dh/v values that are not identical to non-zero
    h/vDiff properties of the - rankObj - then those values
    are assumed to not represent scrollbar thickness and
    the - testObj - can be eliminated form consideration:-
    */
    (
    !(
    (
    dh&&(rankObj.hDiff||(rankObj.hDiff = dh))&&
    (dh != rankObj.hDiff)
    )||(
    dv&&(rankObj.vDiff||(rankObj.vDiff = dv))&&
    (dv != rankObj.vDiff)
    )
    )
    )
    ){
    /* If the - dv - and - dh - values are equal then either
    the browser is not displaying scroll bars at all or
    both the horizontal and vertical scroll bars are being
    displayed (or this object is not reporting the
    viewport/client-region dimensions and the values are
    pure coincidence):-
    */
    if(dh == dv){
    /* If both dh and dv are zero then the likelihood is
    very good that this object is reporting
    viewport/client-region dimensions (and if it is not
    then the values it is reporting can be used without
    trouble anyway), so a zero value is assigned to the
    - rank - property of - rankObj. If the differences
    are non-zero then the likelihood is that both scroll
    bars are showing and have the same dimensions.
    ***
    It is problematic to be assuming that vertical and
    horizontal scrollbars should have the same
    thickness, even though it appears to be true on all
    of the OSs and window managers examined to date
    (this assumption is a significant problem).
    ***
    However, this cannot give the same certainty as zero
    differences so the value of one is assigned.
    */
    rankObj.rank = Number(Boolean(dh));
    /* The assumption that scroll bars will be of the same
    thickens leads to the rule that if either is non-zero
    and dv and dh are not identical then the differences
    cannot represent scrollbars thicknesses and so the
    object cannot be reporting viewport/client-region
    dimensions:-
    */
    }else if((dh&&!dv)||(dv&&!dh)){
    /* When one of the values is zero the sum of the values
    will be the magnitude of the other. This value is
    assigned to the - rank - property of - rankObj -
    and the - testObj - with the smallest corresponding
    - rank - value will be provisionally preferred over
    the other:-
    */
    rankObj.rank = (dh+dv);
    }else{
    /* Otherwise NaN is returned and will result in -
    testObj - being eliminated form consideration as a
    candidate for reading the viewport/client-region
    dimensions:-
    */
    rankObj.rank = NaN;
    }
    }else{
    rankObj.rank = NaN;
    }
    return rankObj;
    }
    /* Provided with an object as its first parameter, and an
    indefinite number of following string arguments, this function
    returns true if all the string arguments represent numeric
    properties of the - testObj - object, and false otherwise.
    It is used to check whether various objects support relevant
    numeric properties before considering them as candidates for
    the reporting of the values of those properties:-
    */
    function propsAreNumbers(testObj){
    for(var c = arguments.length;--c;){
    if(typeof testObj[arguments[c]] != 'number'){
    return false;
    }
    }
    return true;
    }
    /* Initially the following function wraps the preceding -
    rankObj_inner - function, testing that the - testObj -
    parameter supports the relevant properties. However, the -
    rankObj - identifier is assigned a reference to -
    rankObj_inner - after it has been called on each object of
    interest because if they support the relevant properties on
    the first call to - rankObj - they will still support the
    properties on any subsequent calls. There is no need to
    re-execute the code within this function:-
    */
    function rankObj(testObj, rankObj){
    if(
    testObj&&
    propsAreNumbers(testObj, 'clientWidth','clientHeight')
    ){
    rankObj_inner(testObj, rankObj)
    }
    return rankObj;
    }
    /* The default methods of the interface object uses functions that
    call this function as the default values of - getHeight - and -
    getWidth - but this function replaces those functions, with the
    effect that this function is only called once. It does the
    initial set-up for the testing that is used to determine which
    object the viewport/client-region dimensions should be read
    from. This strategy defers the configuration/set-up decisions
    until the first use of one of those methods:-
    */
    function initWidthHeight(callOn){
    lastIW = getWidthI();
    lastIH = getHeightI();
    lastSt = getScrlYMain();
    lastSl = getScrlXMain();
    /* The objects used to hold the decision making information
    that is used to decide between the use of - document.body
    - and - document.documentElement - have their initial -
    rank - property defaulted to NaN so that the corresponding
    object will be eliminated form consideration if it is either
    not implemented or does not support numeric
    clientheight/Width properties:-
    */
    bodyRank = {vDiff:0,hDiff:0,rank:NaN};
    docElRank = {vDiff:0,hDiff:0,rank:NaN};
    /* Replacing the dimension retrieval methods prevents this
    function from being re-called on subsequent uses of the
    interface methods. These replacement functions re-evaluate
    the decision making criteria whenever the browser window is
    innerHeight/Width values change:-
    */
    theInterface.getWidth = getWidthSmart;
    theInterface.getHeight = getHeightSmart;
    /* The scroll value reporting method are swapped for versions
    that test for changes in the scroll values as those changes
    allow an opportunity to re-evaluate the decision making
    criteria:-
    */
    theInterface.getScrollX = getScrlXTest;
    theInterface.getScrollY = getScrlYTest;
    /* The - threeObjectTest - function attempts to make the
    decision between reading the clientHeight/Width values form
    either the - document.body - or the -
    document.documentElement -, or the innnerHeight/Width
    properties from the global object:-
    */
    threeObjectTest();
    /* The first use of - threeObjectTest - calls - rankObj -,
    which tests for the existence of - document.body - and -
    document.documentElement -. But if the objects are not
    available on the browser, or do not support the required
    properties they will be eliminated form future
    consideration. as a result it is unecessery to re-test
    their existence, and re-testing is avoided by swapping -
    rankObj - for the function that it calls to acquire the
    actual ranking values:-
    */
    rankObj = rankObj_inner;
    /* Having initialised the configuration process this function
    must return a value. It does this by calling whichever
    corresponding method has been assigned to the interface at
    this point:-
    */
    return theInterface[callOn]();
    }
    /* The strategy used to determine which object is to be used to
    report viewport/client-region dimensions is to generate a
    "ranking" for the body and the documentElement. If that ranking
    is NaN the object can immediately be removed form consideration.
    Otherwise the object with the _lowest_ ranking is used
    provisionally, and the tests repeated if the browser window is
    re-sized or scrolled (as that means that the decision making
    criteria have changed and it might have become possible to make
    a more final decision):-
    */
    function threeObjectTest(){
    /* Whichever function is currently assigned to - rankObj - is
    called and a boolean value set based on whether the - rank
    - property of the returned object (bodyRank or docElRank)
    is NaN:-
    */
    var bodyNaN = isNaN(rankObj(document.body, bodyRank).rank);
    var docElNaN =
    isNaN(rankObj(document.documentElement, docElRank).rank);
    /* If either value is true (a - rank - was NaN) at least one of
    the objects can be removed from further consideration:-
    */
    if(bodyNaN||docElNaN){
    /* If both values were NaN then only reading the
    innerHeight/Width values of the global object is viable
    so the interface methods are re-assigned to functions
    that do that job directly an do no further testing:-
    */
    if(bodyNaN&&docElNaN){
    setMinimumScroll();
    setInnerWH();
    /* Else only one object can be dropped from consideration
    (leaving that object and reading the innerHeight/Width
    from the global object as the only remaining options):-
    */
    }else{
    /* The non-NaN returning object is assigned to -
    readSizeC - and its corresponding 'ranking' object
    assigned to - bodyRank -:-
    */
    readSizeC = ((docElNaN)?document.body:
    document.documentElement);
    bodyRank = ((docElNaN)?bodyRank:docElRank);
    docElRank = null;
    /* Having eliminated one of the objects it is only
    necessary to make decisions between the two
    remaining candidates. This is done by swapping the
    - threeObjectTest - function for one that only makes
    decisions between whichever object has been assigned
    to - readSizeC - and using innerHeight/Width
    values:-
    */
    threeObjectTest = twoObjectTest;
    }
    /* If neither object can be rejected at this stage one of them
    must be chosen provisionally to be used to return
    veiwport/client-region dimensions. The object with the
    lowest 'ranking' is used, and that decision re-evaluated
    whenever the browser has been re-sized or scrolled:-
    */
    }else{
    readSizeC = ((bodyRank.rank < docElRank.rank)?
    document.body:
    document.documentElement);
    }
    }
    /* Once one object has been eliminated it is only necessary to test
    the remaining object to see if it possible to either eliminate
    it and use the innerHeight/width values of the global object, or
    to settle on that object as the best candidate and so stop
    further testing and remove the global object from consideration.
    This function does those tests:-
    */
    function twoObjectTest(){
    /* If the - rank - is Nan after this test then the remaining on
    of body or documentElement can be eliminated and only the
    innerHeight/Width values reported in future:-
    */
    if(isNaN(rankObj(readSizeC, bodyRank).rank)){
    bodyRank = null;
    setMinimumScroll();
    setInnerWH();
    /* Else if the one remaining object has seen non-zero
    differences in clientWidth/Height and innerWidth/Height
    (scroll bar thicknesses) and has survived this test twice
    then it is a fair bet that it does represent the best object
    to be reading viewport/client-region dimensions from. So
    further consideration of the innerHeight/Width values is
    eliminated, along with any additional testing:-
    */
    }else if(
    (bodyRank.vDiff)&&(bodyRank.hDiff)&&
    (++twoTestCount > 2)
    ){
    bodyRank = null;
    theInterface.getWidth = getWidthC;
    theInterface.getHeight = getHeightC;
    setMinimumScroll();
    }
    }
    /* The following code is executed when the outermost function is
    called. It is only executed once as its final act is to assign
    an inner function to - getWindowState -, that inner function
    then acts as the 'factory' function for the interface:-
    */
    /* If the global object does not support innerHeight/Width
    properties then the preceding decision making mechanism cannot
    be used. Instead a decision is made at this point based on the
    usual - document.compatMode - tests that apply to IE browser:-
    */
    if(!propsAreNumbers(global, 'innerHeight', 'innerWidth')){
    readSizeC = compatModeTest(readSizeC);
    theInterface.getWidth = getWidthC;
    theInterface.getHeight = getHeightC;
    }else{
    /* As the global object does supports innerHeight/Width the
    default - readSizeI - object is replaced with a reference
    to the global object:-
    */
    readSizeI = global;
    }
    /* If the global object supports pageXYOffset properties then
    they are the simplest mechanism for reporting scroll values:-
    */
    if(propsAreNumbers(global, 'pageYOffset', 'pageXOffset')){
    readScroll = global;
    readScrollY = 'pageYOffset';
    readScrollX = 'pageXOffset';
    /* Else the - document.compatMode - tests are used again:-
    */
    }else{
    readScroll = compatModeTest(readScroll);
    }
    /* The global reference to - getWindowState - is replaced with a
    reference to an inner function that just returns the interface
    object, and that inner function is called and its return value
    returned by this initial (and only) cal to the current -
    getWindowState - (outermost) function:-
    */
    return (getWindowState = function(){return theInterface;})();
    }

    /* The preceding function is dependent upon this compatMode
    test function (at least in IE and IE-like browsers):-
    */
    function compatModeTest(obj){
    if(
    (document.compatMode)&&
    (document.compatMode.indexOf('CSS') != -1)&&
    (document.documentElement)
    ){
    return (compatModeTest = function(){
    return document.documentElement;
    })((obj = null));
    }else if(document.body){
    return (compatModeTest = function(){
    return document.body;
    })((obj = null));
    }else{
    return obj;
    }
    }

    OK that is a huge mass of (convoluted :) code, made worse by being
    extensively commented. However, properly minimised it reduces to <
    2.4Kb, and its complexity is internal, the public interface is small and
    simple, making it easy to use without any interest in its internal
    complexity.

    Comments/feedback/Improvements/etc welcome.

    Richard.
     
    Richard Cornford, Sep 11, 2004
    #3
  4. Richard Cornford wrote:

    > Personally, I don't like the answer to 4.9 much anyway.


    After studying your script I can understand why indeed, the FAQ code
    uses too simplified a logic; I cannot tell to which extent this can be
    dangerous, though.

    > Unfortunately the very simple rules that apply to determining which
    > element should be read to retrieve the viewport/visible client-region
    > (inside any scroll bars) dimensions on IE browsers are not really
    > applicable to any other DOM browsers (except by coincidence on some).


    It had been time since I hadn't done extended browser testing, so I've
    written some test case and tried to watch all the properties which could
    be used in determining the client dimensions, with different compat
    modes; I have learnt things, especially with Opera, which I (naively)
    thought behaved the same way as Mozilla.

    > For example, the real viewport/client-region
    > dimensions will never exceed the corresponding innerWidth/Height
    > dimensions (only the document dimensions can do that, though they may
    > not), and if a page is scrolled it is reasonable to assume that it has a
    > scroll bar, which means that in that case the 'client' dimension inside
    > the scroll bar must be smaller than the corresponding 'innner' value.


    That's an excellent hypothesis, which permits, with the ranking process,
    a finer approach; at least it is much more satisfying than the pure
    compatMode-based one (which relates the value of a property 'compatMode'
    to another 'dimensions' - that goes against good feature detection).

    > This brings me to the progressively self re-configuring ("Russian doll")
    > object/system that I said I would publish a few months back. That object
    > was intended to address this particular problem.


    I'm starting to see the interest of the technique from a conception
    point of view, beyond the simple init/replace issue. The resulting
    structure and flow are elegant and attractive, however it can easily
    turn to something complicated (it took me some time to figure out why
    your smart size getting didn't recurse forever); I wonder to which
    extent the russian doll pattern could be formally structured, using
    objects and setters.

    > I still haven't had
    > time to fully test it (at least to the extent that I would consider
    > necessarily), it hasn't yet been shown to any Mac browsers, and I don't
    > think Konqueror has seen this current version yet either.


    I don't have Konqueror nor Mac browsers here, but I've tested it on
    'regular' browsers (IE4, IE5, IE5.5, IE6, NN4.8, Mozilla 1.8, Opera 6,
    Opera 7) and it gave the safest results for scripting; the logic behind
    the script is strong enough to make me confident it should behave
    correctly in other agents, provided they respect the inner/client
    hypothesis.

    > function getScrlXTest(){hasChangedScroll();return getScrlXMain();}
    > function getScrlYTest(){hasChangedScroll();return lastSl;}


    One or the other is the same, lastSl is faster but getScrlYMain is
    easier to read.

    > OK that is a huge mass of (convoluted :) code, made worse by being
    > extensively commented.


    Ah I was thankful for these comments however! I've spent a few hours on
    the code, testing and reading and experimenting, and I'm still not
    satisfied with my study!

    > Comments/feedback/Improvements/etc welcome.


    A truly rich script, mixing an innovative approach to client dimensions
    measuring, and producing an advanced "russian doll" initialization; both
    areas definitely deserve further investigating.


    Cheers,
    Yep.
     
    Yann-Erwan Perio, Sep 12, 2004
    #4
  5. Yann-Erwan Perio wrote:
    > Richard Cornford wrote:
    >> Personally, I don't like the answer to 4.9 much anyway.

    >
    > After studying your script I can understand why indeed,
    > the FAQ code uses too simplified a logic; I cannot tell
    > to which extent this can be dangerous, though.


    Code that is logically the same as the example in the FAQ has given me
    the flickering scroll bar problem that I described. Clearly the FAQ code
    cannot be an issue for most of the people using it, else it would have
    attracted more comment in the past. But then it gives the
    viewport/client-region dimensions on IE so maybe that is down to limited
    cross-browser testing.

    <snip>
    > ... ; I have learnt things, especially with Opera, which
    > I (naively) thought behaved the same way as Mozilla.


    One of the problems with Opera 7 is that behaviour has changed with
    progressive versions. I am not sure whether they are migrating toward
    Mozilla-like, or IE-like, behaviour.

    >> For example, the real viewport/client-region dimensions
    >> will never exceed the corresponding innerWidth/Height
    >> dimensions (only the document dimensions can do that, though
    >> they may not), and if a page is scrolled it is reasonable to
    >> assume that it has a scroll bar, which means that in that
    >> case the 'client' dimension inside the scroll bar must be
    >> smaller than the corresponding 'innner' value.

    >
    > That's an excellent hypothesis,


    As it stands, the algorithm used is about the third variant that I have
    attempted to apply to the problem, and the most successful to date.
    There may still be room for improvement, though just throwing in
    additional test criteria may not be an improvement as that may slow down
    the reading/reporting of the values by the interface, and I want that to
    be as fast as possible.

    > which permits, with the ranking process, a finer approach;
    > at least it is much more satisfying than the pure
    > compatMode-based one (which relates the value of a property
    > 'compatMode' to another 'dimensions' - that goes against
    > good feature detection).


    The compatMode test is not good feature detection. It is almost 'object
    inference', except that the relationship is well documented for IE
    browsers so there it is not that much of an inference. Indeed the code
    in 4.9 doesn't use a compatMode test, though the code it does use makes
    assumptions about the values of clientWidth/Height that are true for IE
    but will fail horribly on some other browsers (which never execute that
    code branch as others seem to universally support innerWidth/Height).

    >> This brings me to the progressively self re-configuring
    >> ("Russian doll") object/system that I said I would
    >> publish a few months back. That object was intended to
    >> address this particular problem.

    >
    > I'm starting to see the interest of the technique from a
    > conception point of view, beyond the simple init/replace
    > issue. The resulting structure and flow are elegant and
    > attractive, however it can easily turn to something
    > complicated (it took me some time to figure out why your
    > smart size getting didn't recurse forever); I wonder to
    > which extent the russian doll pattern could be formally
    > structured, using objects and setters.


    Complexity is an issue, but I think that so long as it is internal to an
    object/function that has a simple, cohesive interface then it is less of
    a problem. At least so long as the code works as advertised and nobody
    has to go in and fix it. Well designed cross-browser code should satisfy
    that requirement as its logic should enable it to exhibit
    planned/designed behaviour in any environment.

    One of the reasons that I have been writing this type of functionality
    as independent low-level interface objects is that the test-case pages
    for a single interface can act as unit tests, and once properly tested I
    can be confident that I have a unit of reliable, re-usable code that
    _will_ exhibit planned/designed behaviour in any browser environment.
    (It also means that when I can be certain that precise
    viewport/client-region dimensions are not necessary, and
    innerWidth/Height will be good enough, I can use an alternative
    implementation of the same interface that employs the simpler
    configuration/testing. Without having to alter a line of any code using
    the interface.)

    As to formally structuring the "Russian doll" pattern; I haven't
    written/tested enough examples yet to be certain that I have a good
    handle on the issues. Another couple of generations and it might be
    worth writing up, and publishing, a description of the strategy.

    Though I don't think I will want to connect the pattern exclusively with
    objects and getters/setters, as a pattern where the global manifestation
    is always no more than an identifier referring to a function object
    (such as the compatModeTest function needed by getWindowState) seems
    like a reasonable application as well.

    I would encourage the use of low-level interface object, which mostly
    will have getters and setters, but that represents a line of thought
    that is simultaneous with (and will tend to employ) the "Russian doll"
    pattern, rather than an integral part of the strategy itself.

    <snip>
    > I don't have Konqueror nor Mac browsers here,


    If you have 4 or 5 hours use of a broadband connection (or better, for
    less time) and a CD burner, then you might like to look into Knoppix
    (<URL: http://knoppix.com >). A Linux version designed for use from a
    self-booting CD ROM (you download the ISO image of the disk (which is
    why it takes some time (640Mb)), including KDE with whatever is the
    latest version of Konqueror. The version I am using (3.6) has
    successfully booted about 3 out of 4 of the x86 desktop PCs that I have
    tried it on to date. I am also finding the when something works on
    Konqueror 3 then it also tends to work on Mac Safari (though the reverse
    probably isn't true).

    > but I've tested it on 'regular' browsers (IE4, IE5, IE5.5,
    > IE6, NN4.8, Mozilla 1.8, Opera 6, Opera 7) and it gave the
    > safest results for scripting; the logic behind the script
    > is strong enough to make me confident it should behave
    > correctly in other agents, provided they respect the
    > inner/client hypothesis.


    I am fairly sure that the logic is sound (I should have traced out all
    of the possibilities by now) but I have been caught being over-confident
    before so I am not going to claim it is safe to use until I have
    finished testing it.

    >> function getScrlXTest(){hasChangedScroll();return getScrlXMain();}
    >> function getScrlYTest(){hasChangedScroll();return lastSl;}

    >
    > One or the other is the same, lastSl is faster but getScrlYMain
    > is easier to read.


    The hasChangedScroll function is guaranteed to have called getScrlYMain
    and assigned the current value to lastSl. I am interested in having the
    interface methods return their results as quickly as possible so
    returning lastSt from getScrlYTest avoids re-calling getScrlYMain but
    the same cannot be done for getScrlXMain/lastSt as it might not have
    been updated in the call to hasChangedScroll.

    However, there is a minor mistake in this code, and in -
    hasChangedScroll -, as it was my intention that the 'l' at the end of
    lastSl represent 'left' (similarly the 't' in lastSt for 'top'), so the
    two variables are being used the wrong way around here and the right way
    around in the - initWidthHeight - function. That doesn't make much
    difference in practice (both X and Y scrolls are likely to be zero on
    first use anyway, and otherwise the error will just re-run the test
    functions once), but it is still wrong and those two lines should be:-

    function getScrlXTest(){hasChangedScroll();return getScrlXMain();}
    function getScrlYTest(){hasChangedScroll();return lastSt;}

    - with the - hasChangedScroll - function becoming:-

    function hasChangedScroll(){
    if(
    (lastSt != (lastSt = getScrlYMain()))||
    (lastSl != (lastSl = getScrlXMain()))
    ){
    threeObjectTest();
    }
    }

    (While writing this I was happy to observe that the algorithm for the
    not-equals comparison allows for the comparison of current and last
    values, along with the update to the single variable used, in one
    expression.)

    >> OK that is a huge mass of (convoluted :) code, made
    >> worse by being extensively commented.

    >
    > Ah I was thankful for these comments however! ...


    Yes, it would hardly have been fair to ask anyone to look at it without
    some comments. But ultimately the sensible way of dealing with the
    shortcomings in 4.9 will be to do what was done for 4.15 and provide an
    additional page which describes the issues and offers at least one
    example of avoiding them. For which I will need a fully (probably more
    fully) commented version.

    >> Comments/feedback/Improvements/etc welcome.

    >
    > A truly rich script, mixing an innovative approach to
    > client dimensions measuring, and producing an advanced
    > "russian doll" initialization; both areas definitely
    > deserve further investigating.


    Thank you, I look forward to seeing some implementations of self
    re-configuring functions/objects of yours.

    Richard.
     
    Richard Cornford, Sep 13, 2004
    #5
  6. Richard Cornford wrote:

    > Complexity is an issue, but I think that so long as it is internal to an
    > object/function that has a simple, cohesive interface then it is less of
    > a problem. At least so long as the code works as advertised and nobody
    > has to go in and fix it. Well designed cross-browser code should satisfy
    > that requirement as its logic should enable it to exhibit
    > planned/designed behaviour in any environment.


    I had more in mind maintenance/study of the component itself by multiple
    programmers, however it's true that such a component, once written, is
    unlikely to be changed.

    > Though I don't think I will want to connect the pattern exclusively with
    > objects and getters/setters, as a pattern where the global manifestation
    > is always no more than an identifier referring to a function object
    > (such as the compatModeTest function needed by getWindowState) seems
    > like a reasonable application as well.


    That seems fair; since I haven't experimented much with the pattern I'm
    probably just being a bit overly prudent with the manipulation; I guess
    that building 2-3 components will give me a clearer vision.

    > If you have 4 or 5 hours use of a broadband connection (or better, for
    > less time) and a CD burner, then you might like to look into Knoppix
    > (<URL: http://knoppix.com >).


    Currently downloading; I've never used Linux before, so that'll be fun
    anyway - I'll just think about doing a full back-up before installing
    the thing:)

    > The hasChangedScroll function is guaranteed to have called getScrlYMain
    > and assigned the current value to lastSl. I am interested in having the
    > interface methods return their results as quickly as possible so
    > returning lastSt from getScrlYTest avoids re-calling getScrlYMain but
    > the same cannot be done for getScrlXMain/lastSt as it might not have
    > been updated in the call to hasChangedScroll.


    I missed that, unfortunately - I'll have to review the code a bit more
    carefully. I appreciate the speed argument:)

    > (While writing this I was happy to observe that the algorithm for the
    > not-equals comparison allows for the comparison of current and last
    > values, along with the update to the single variable used, in one
    > expression.)


    Well that's indeed a beautiful expression, a bit unexpected though (one
    would at first sight evaluate the inner assignment before the
    comparison, though ECMA262 evaluates the relational expression before
    the shift expression, as in 11-8).

    I myself love playing with expressions, the last one I enjoyed writing was

    if((function() { /*...*/ })()) {

    > Thank you, I look forward to seeing some implementations of self
    > re-configuring functions/objects of yours.


    I'll play with the Russian Doll for sure, and believe others (especially
    Mike) will as well.


    Regards,
    Yep.
     
    Yann-Erwan Perio, Sep 13, 2004
    #6
  7. [OT] Knoppix (was Re: Browser Height in IE? NOT document height)

    On Mon, 13 Sep 2004 21:18:23 +0200, Yann-Erwan Perio
    <> wrote:

    > Richard Cornford wrote:
    >
    >> If you have 4 or 5 hours use of a broadband connection (or better, for
    >> less time) and a CD burner, then you might like to look into Knoppix
    >> (<URL: http://knoppix.com >).

    >
    > Currently downloading; I've never used Linux before, so that'll be fun
    > anyway - I'll just think about doing a full back-up before installing
    > the thing:)


    My impression of Knoppix was that there is no installation. In fact, I
    don't think it's possible to install it. Knoppix is a self-contained Linux
    distribution on a CD. The absence of disk space recommendations in the
    requirements list would imply that.

    Regarding the back-up, I don't think it would be necessary, but if you've
    got time to kill... However, one thing that you probably shouldn't try to
    do is write to an NTFS disk from Linux. The last time I checked, reading
    support was OK, but writing was experimental. I keep a FAT partition
    around in case I want to pass information between my OSs.

    [snip]

    > I'll play with the Russian Doll for sure, and believe others (especially
    > Mike) will as well.


    Me?

    Mike

    --
    Michael Winter
    Replace ".invalid" with ".uk" to reply by e-mail.
     
    Michael Winter, Sep 13, 2004
    #7
  8. Re: [OT] Knoppix (was Re: Browser Height in IE? NOT document height)

    Michael Winter wrote:

    > Regarding the back-up, I don't think it would be necessary, but if
    > you've got time to kill


    Nah, just kidding, the computer here is regularly backed-up anyway, I
    was just "overly prudent" as with anything new:)

    >... However, one thing that you probably
    > shouldn't try to do is write to an NTFS disk from Linux. The last time
    > I checked, reading support was OK, but writing was experimental. I keep
    > a FAT partition around in case I want to pass information between my OSs.


    Thanks for the info; luckily that happens to be the case there.

    >> I'll play with the Russian Doll for sure, and believe others
    >> (especially Mike) will as well.


    > Me?


    I was indeed thinking of you based on my reading of your code; you're
    one of the few here to experiment with function expressions, and you've
    also demonstrated a certain interest for javascript 'expressiveness'
    (for instance you regularly use naturally 'inner assignments') - so I
    believe the Russian Doll should appeal to you (all the more it appears
    to be a javascript-specific pattern and not only a simple js technique).


    Regards,
    Yep.
     
    Yann-Erwan Perio, Sep 14, 2004
    #8
  9. Re: [OT] Knoppix (was Re: Browser Height in IE? NOT document height)

    Michael Winter wrote:
    > Yann-Erwan Perio wrote:
    >> Richard Cornford wrote:
    >>> ... , then you might like to look into
    >>> Knoppix (<URL: http://knoppix.com >).

    >>
    >> Currently downloading; I've never used Linux before, so
    >> that'll be fun anyway - I'll just think about doing a full
    >> back-up before installing the thing:)

    >
    > My impression of Knoppix was that there is no installation.


    There is no installation. By default knoppix is self-contained on the
    self-booting CD and uses a RAM disk for temporary storage.

    > In fact, I don't think it's possible to install it.


    It was my impression that it could be installed, though it would
    probably less effort to acquire a Linux version intended for PC
    installation.

    > Knoppix is a self-contained Linux distribution on a CD.
    > The absence of disk space recommendations
    > in the requirements list would imply that.


    Available RAM is probably the most significant consideration, though KDE
    benefits form fast CPUs.

    > Regarding the back-up, I don't think it would be necessary,
    > but if you've got time to kill... However, one thing that
    > you probably shouldn't try to do is write to an NTFS disk
    > from Linux. The last time I checked, reading support was
    > OK, but writing was experimental. I keep a FAT partition
    > around in case I want to pass information between my OSs.


    By default Knoppix has read-only access to the local hard disks, so it
    should be safe. There are options for allowing it write access, and for
    arranging persistent storage (of configuration/set-up details, etc) on a
    local hard disk. For testing with Konqueror alone that has not proved
    necessary, though the reporting of javascript errors, and the javascript
    debugger, are disabled by default on the 3.6 disk so it might be nice to
    have the enabled settings persist between Linux sessions).

    > [snip]
    >
    >> I'll play with the Russian Doll for sure, and believe
    >> others (especially Mike) will as well.

    >
    > Me?


    Why not you? ;-)

    Richard.
     
    Richard Cornford, Sep 14, 2004
    #9
  10. Yann-Erwan Perio wrote:
    > Richard Cornford wrote:
    >> Complexity is an issue, ...

    <snip>
    > I had more in mind maintenance/study of the component
    > itself by multiple programmers, however it's true that
    > such a component, once written, is unlikely to be changed.


    I had intended to mention that that example of the application of a
    "Russian doll" pattern is probably among the most complex needed. The
    progressive configuration that had to be applied to that problem is
    unusual and most applications will be able to fully re-configure
    themselves on their first use in one step.

    I should comment and post a more direct application, but I don't have
    time right now (maybe later in the week).

    <snip>
    >> (While writing this I was happy to observe that the algorithm
    >> for the not-equals comparison allows for the comparison of
    >> current and last values, along with the update to the single
    >> variable used, in one expression.)

    >
    > Well that's indeed a beautiful expression, a bit unexpected
    > though (one would at first sight evaluate the inner assignment
    > before the comparison, though ECMA262 evaluates the relational
    > expression before the shift expression, as in 11-8).

    <snip>

    Fortunately the ECMA algorithm seems to have been consistently
    implemented in browsers (that was my main concern about the expression).

    Richard.
     
    Richard Cornford, Sep 14, 2004
    #10
  11. Re: [OT] Knoppix (was Re: Browser Height in IE? NOT document height)

    On Tue, 14 Sep 2004 01:36:41 +0100, Richard Cornford
    <> wrote:

    [snip]

    > It was my impression that [Knoppix] could be installed, though it would
    > probably less effort to acquire a Linux version intended for PC
    > installation.


    You're right. It can be installed. Instructions are given in the last FAQ
    entry. If it's not on the CD, you can read it on one of the mirrors. For
    example, <URL:ftp://ftp.uni-kl.de/pub/linux/knoppix/KNOPPIX-FAQ-EN.txt>.

    [snip]

    > By default Knoppix has read-only access to the local hard disks, so it
    > should be safe. There are options for allowing it write access, and for
    > arranging persistent storage (of configuration/set-up details, etc) on a
    > local hard disk.


    The FAQ mentions that, too. Including the warning I gave regarding NTFS
    usage.

    > For testing with Konqueror alone that has not proved necessary, though
    > the reporting of javascript errors, and the javascript debugger, are
    > disabled by default on the 3.6 disk so it might be nice to have the
    > enabled settings persist between Linux sessions).


    It would probably be easier to save and script modifications, rather than
    writing, printing, or remembering them, too.

    That reminds me. I should really stop fiddling with Linux and just get it
    up and running so I can use it again.

    [Yep:]

    >>> I'll play with the Russian Doll for sure, and believe
    >>> others (especially Mike) will as well.

    >>
    >> Me?

    >
    > Why not you? ;-)


    Well, for one thing, I haven't read what you two have been writing about
    yet. :p

    I suppose I'm just surprised to be mentioned directly.

    Mike

    --
    Michael Winter
    Replace ".invalid" with ".uk" to reply by e-mail.
     
    Michael Winter, Sep 15, 2004
    #11
  12. Re: [OT] Knoppix

    Richard Cornford wrote:

    > Michael Winter wrote:
    >> My impression of Knoppix was that there is no installation.

    >
    > There is no installation. By default knoppix is self-contained on the
    > self-booting CD and uses a RAM disk for temporary storage.
    >
    >> In fact, I don't think it's possible to install it.

    >
    > It was my impression that it could be installed, though it would
    > probably less effort to acquire a Linux version intended for PC
    > installation.


    Firstly, AFAIS Knoppix is nothing but a Debian (<http://debian.org/>)
    GNU/Linux based system using on-boot hardware detection to create the
    necessary configuration files on the RAM disk. Besides saving
    configuration files on a block device usually harddisk partition, e.g.)
    and restoring them on boot as well as maintaining a home directory on any
    block device, it can be installed on a harddisk and runs from there (see
    <http://knopper.net/>). The difference is that further modifications in
    system configuration files would be required to have the system use the
    preconfigured files and skip the hardware detection.

    Secondly, Knoppix (or any other so-called "Linux distribution") is not
    a Linux version (the kernel) alone, it is the kernel and corresponding
    software (like system commands and a command shell). That's why it is
    correctly called "GNU/Linux", e.g., where GNU (GNU's Not Unix) is the
    operating system and Linux is its kernel. There is no "Linux version
    intended for PC installation", precompiled kernels aside (they were not
    meant by posters in this discussion). See <http://www.gnu.org/>.


    PointedEars
    --
    Every knee shall bend, every mouth open, when I unzip
     
    Thomas 'PointedEars' Lahn, Sep 17, 2004
    #12
  13. Re: [OT] Knoppix (was Re: Browser Height in IE? NOT document height)

    Michael Winter wrote:
    > Richard Cornford wrote:

    <snip>
    >> For testing with Konqueror alone that has not proved
    >> necessary, though the reporting of javascript errors,
    >> and the javascript debugger, are disabled by default
    >> on the 3.6 disk so it might be nice to have the
    >> enabled settings persist between Linux sessions).

    >
    > It would probably be easier to save and script modifications,
    > rather than writing, printing, or remembering them, too.


    For small changes saving to a floppy disk is practical. I am usually not
    running Knoppix on my own hardware so playing with local hard disks
    would not necessarily be popular.

    <snip>
    >>>> I'll play with the Russian Doll for sure, and
    >>>> believe others (especially Mike) will as well.
    >>>
    >>> Me?

    >>
    >> Why not you? ;-)

    >
    > Well, for one thing, I haven't read what you two
    > have been writing about yet. :p

    <snip>

    We are talking about a strategy for maximising the efficiency of feature
    detection tests by avoiding repeating test when they would produce the
    same results on any subsequent use, and doing this by having the first
    use of a function (or a method of an object) re-configure itself based
    on the tests so that subsequent calls are able to directly use code
    tailored to the browser environment in use.

    So where a function might do:-

    function getElementWithId(id){
    var obj = null;
    if(document.getElementById){
    obj = document.getElementById(id);
    }else if(document.all){
    obj = document.all[id];
    }else if(document.layers){
    obj = document.layers[id];
    }
    return obj;
    }

    - a self-reconfiguring alternative might go:-

    function getElementWithId(id){
    var fnc = function(){return null;};
    if(document.getElementById){
    fnc = function(id){
    return document.getElementById(id);
    };
    }else if(document.all){
    fnc = function(id){return document.all[id];};
    }else if(document.layers){
    fnc = function(id){return document.layers[id];};
    }
    return (getElementWithId = fnc)(id);
    }

    - so the configuration is only done the first time the function is
    called and form then on the public face of the function has been
    replaced by one of its inner functions (hence the name "Russian doll")
    and that inner function will be the one that is optimum for the current
    browser environment.

    In the case of this example it doesn't make much difference whether this
    style is used or something more like:-

    var getElementWithId;
    if(document.getElementById){
    getElementWithId = function(id){
    return document.getElementById(id);
    }
    }else if(document.all){
    getElementWithId = function(id){
    return document.all[id];
    }
    }else if(document.layers){
    getElementWithId = function(id){
    return document.layers[id];
    }
    }else{
    getElementWithId = function(){
    return null;
    }
    }

    - or:-

    var getElementWithId = (function(){
    if(document.getElementById){
    return (function(id){
    return document.getElementById(id);
    });
    }else if(document.all){
    return (function(id){
    return document.all[id];
    });
    }else if(document.layers){
    return (function(id){
    return document.layers[id];
    });
    }else{
    return (function(){
    return null;
    });
    }
    })();

    - because the decision making criteria are fixed and can be detected at
    any point where it is possible to import a JS file. Though the
    penultimate; test-inline and assign a function to a property of the
    global object, source code seems less of a well-defined unit.

    A function that re-configures itself is probably of most obvious use
    when something needed to perform the test meaningfully is not always
    available. The client-area dimension reading code, for example, needs
    the document.body to be available in order to consider it as a source
    for the required information. And the document.body element does not
    exist until the opening BODY tag has been encountered by the HTML
    parser. That would require inline configuration code to be physically
    within the body. Deferring configuration until the first use of the
    interface allows the code to be imported in the head of the document (so
    appear in a single site-wide JS file) and arrange that it is just its
    first use that doesn't happen until the BODY element exists.

    It might be that the code needs to do its feature detection based on the
    properties of an Element that would be provided to the function by
    reference. The code can either branch internally, re-testing on each
    use, or it can perform the tests the first time it is used and then
    re-configure itself to expose an inner function that acts upon an
    element argument without re-confirming the same features exist on
    subsequent similar elements.

    The deferring of the configuration also means that if the code that uses
    the function decides that as a result of other testing it is not viable
    it may never call the original outer function, and so the configuration
    code will not be executed needlessly.

    The downside to the example self-re-configuring function above is that
    it will form a closure and so some work will be needed to ensure that
    that closure does not trap references that may induce the memory leak
    problem in IE (though mostly element references in such closures should
    be one-way and so not cause any problems). However, because the
    technique will produce a closure, whenever such a closure could be
    directly exploited the design can consider an implementation that
    follows this pattern. Many examples of the in-line execution of a
    function expression that returns a function can be alternatively
    implemented in this pattern. It lends itself to code that is using an
    outer function to encapsulate related functionality, it just defers the
    creation of the associated closure.

    It also allows for the multi-stage configuration that the
    dimension-reporting object uses, while keeping the resulting code in the
    form of a self-contained unit.

    Richard.
     
    Richard Cornford, Sep 20, 2004
    #13
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Replies:
    0
    Views:
    557
  2. MAX
    Replies:
    7
    Views:
    225
    A. Sinan Unur
    Dec 21, 2005
  3. Don Vaillancourt

    How to tell height of div where height is not set?

    Don Vaillancourt, Jan 28, 2005, in forum: Javascript
    Replies:
    8
    Views:
    146
    Dietmar Meier
    Jan 31, 2005
  4. icogs
    Replies:
    0
    Views:
    139
    icogs
    Jan 31, 2008
  5. Martin Molema
    Replies:
    2
    Views:
    148
    dhtml
    Jan 11, 2009
Loading...

Share This Page