David Mark's Javascript Tip Du Jour - Volume #1 - Tip #1234 - How toMeasure Element Dimensions

Discussion in 'Javascript' started by David Mark, Nov 10, 2011.

  1. David Mark

    David Mark Guest

    How to Measure Element Dimensions

    Typically these functions fall into three categories: call them
    getOuterDimensions, getInnerDimensions, and getCssDimensions. The
    first two refer to the size outside and inside the border respectively
    and the last the CSS dimensions (per the box model).

    The first two have just one rendition each. With simplified feature
    detection*:-

    if (document.documentElement && typeof
    document.documentElement.offsetWidth == 'number') {
    var getOuterDimensions = function(el) {
    return [el.offsetHeight, el.offsetWidth];
    };
    }


    if (document.documentElement && typeof
    document.documentElement.clientWidth == 'number') {
    var getInnerDimensions = function(el) {
    return [el.clientHeight, el.clientWidth];
    };
    }

    Those never change. They work even in IE 4. Due to the host feature
    detection and conditional creation of these functions, applications
    that do proper API feature detection will degrade gracefully in IE 3.
    Of course, if the application's script has a try-catch, the
    degradation path for IE 4 and under is a parse error.

    As for getCssDimensions, a GP solution may (and often does) use
    getComputedStyle, but unfortunately that method has been notoriously
    buggy over the years and the older versions of IE (under 9) don't
    support it at all. You could try to string together a bunch of hacks
    to emulate getComputedStyle with IE's various proprietary objects, but
    it's a fool's errand.

    Here is a relatively slow solution that works for all box models in
    "all browsers":-

    http://www.cinsoft.net/size.html

    As with virtually everything in cross-browser scripting, defining a
    specific context is the key to creating a reliable function.

    What if the design decision was made to measure only elements with the
    border-box box model? Then you could do this:-

    var getCssDimensions = getOuterDimensions;

    Unfortunately, that is an inappropriate solution for most contexts as
    many browsers do not support that box model. For example, IE 5-8 will
    do it only in quirks mode (which is always best avoided).

    What if the design decision was made to measure only elements with the
    content-box box model *and* without padding? Then you could do this:-

    var getCssDimensions = getInnerDimensions;

    This is an excellent (and relatively fast) solution as the default for
    virtually every element in virtually every modern browser is content-
    box. When designing an application's widgets, simply take care to only
    measure elements without padding. It's not hard to do and the payoff
    is a fast, concise and cross-browser solution. Just make sure to
    document the context as the documentation is as much a part of the
    rendition as the code.

    How *Not* to Measure Element Dimensions

    And what of the jQuery height/width functions? They don't work at all
    for border-box. They also rely on getComputedStyle (or a long-winded,
    over-complicated simulation). It's the worst of both worlds; also
    confusing, relatively slow, poorly documented, failure-prone and
    subject to change at any time on a whim. That's what *defines* jQuery
    (and the like). In its favor... uh, lots of Web developers use it to
    churn out horrible Websites.

    To borrow a term from the jQuery fanboy resistance/marketing
    department: does this sound like FUD to you? I think you should
    certainly be afraid of such doubtful material as jQuery, at least
    until you understand the employed techniques well enough to avoid it
    for rational reasons. Fear of the unknown can be healthy and most
    jQuery users have no idea what it does.

    http://en.wikipedia.org/wiki/Cargo_cult_programming

    * Use isHostObjectProperty (or the like) to detect
    document.documentElement

    http://www.cinsoft.net/
    http://www.twitter.com/cinsoft
    http://jsperf.com/browse/david-mark
     
    David Mark, Nov 10, 2011
    #1
    1. Advertisements

  2. David Mark

    J.R. Guest

    I've learned that you've posted a simplified version of the code
    available on "My Library". So I think it's necessary to remark that in
    IE the HTML element (document.documentElement) is used for the
    viewport in "standards mode" (also known as "strict mode") and the body
    (document.body) is used in "quirks mode" (HTML is not rendered -
    document is displayed as it was displayed in previous versions of IE).

    Therefore document.documentElement.clientHeight (in strict mode) may
    output a slightly different value from document.body.clientHeight (in
    quirks mode).

    I've also noticed that you have taken care of those differences at
    <http://www.cinsoft.net/viewport.asp>.
     
    J.R., Nov 10, 2011
    #2
    1. Advertisements

  3. David Mark

    David Mark Guest

    Which code? The Viewport primer?
    Yes, they may. For example, if the body has a margin.
    Right.

    Just checked MSDN and the offending "community content" entry that I
    had noted a while back is gone. Basically it warned against using the
    clientHeight property because it was "broken" (i.e. returned different
    values in different rendering modes). :)
     
    David Mark, Nov 10, 2011
    #3
  4. David Mark

    David Mark Guest

    Only possible reason is that they don't know they are doing it. ;)
    Well, as a theory it goes way back. In reality, it's a recent (and
    troubling) phenomenon.
    Yes, exactly like jQuery and on more than one level. The jQuery
    project itself is certainly a cargo cult effort. It's locked in a
    design from 2005 that was just as inappropriate for browser scripting
    then as now. And yes, many of their "innovations" over the years have
    been the result of copying patterns without really understanding what
    they were doing.

    jQuery 1.3 was their attempt to replicate my shiny feature testing
    patterns with JS equivalent of bamboo and vines. More recently there
    was the attr/prop overhaul. After years of telling them two things:-

    1. Your attr method makes no sense and is causing a lot of confusion
    2. Here's some realistic prop and attr examples, but it's not the
    exact solution for your needs

    ....they went ahead and split their most low-level DOM API to attr/
    removeAttr and prop/removeProp (almost but not entirely like my
    examples and almost entirely unlike previous jQuery versions). So
    eventually #1 got through, but #2 only partially.

    http://www.cinsoft.net/attributes.html

    They also kinda-sorta rolled back those changes in a later version.
    The details are unclear at this time and will likely remain so. :(

    But for those who cry out that I am not helping them (which is ironic
    as I'm one of the few people who ever tried to help them with this
    mess). For the umpteenth time, the solution to their "attr" problem
    is simple. First you have to define what "attr" is supposed to do.
    To this date, the documentation has never been close to right about
    any of its half-dozen incarnations. In an HTML* DOM, the "attr"
    method gets/sets property values by attribute name. In an XML DOM, it
    gets/sets attribute values by attribute name. That "overloading" is
    unfortunate, but jQuery is stuck with it at this point.

    1. Fix the attr method so that it is at least consistent in the "core
    browsers"
    2. Deprecate removeAttr (it's just confusing as it doesn't go with
    attr).
    3. As companion to form control value getter/setter, add a "checker"
    for checkbox/radio buttons

    The jQuery users don't need a pure attribute getter/setter; and, as
    predicted, they don't want one. Same for removing attributes (or God
    forbid properties). The underlying query engine is another story. To
    support legacy IE versions (and compatibility modes), it must have
    such a function, but never has (and apparently never will). Probably
    doesn't matter as I expect them to stop "caring" about legacy IE any
    day now.

    I think I've posted more than my share of attribute/property-related
    examples over the years, so step 1 should not be a problem for anyone
    inclined to fix the silly thing. The other two take almost no thought
    or effort at all.

    Finally, jQuery users include it in documents as a rule, regardless of
    context or other considerations and often doing more harm than good.
    That's also cargo cult behavior.

    * Would hold true in an XHTML DOM as well if jQuery worked in an XHTML
    DOM
     
    David Mark, Nov 10, 2011
    #4
  5. As a "young 'un", I can tell you that it's a phenomenon
    propped up by the "best practices" methodology.

    "best practices" (sounds terrible when you read it aloud)
    is an arguably dogmatic set of beliefs and observations
    pertaining to a particular programming topic. The crux of
    "best practices" is that it's how "popular" programmers
    (in that particular field) operate. For example,
    "HTML 5 Boilerplate" is a massive set of server
    configurations, CSS file(s) ("normalizing", they call it),
    JavaScript files (including jQuery & modernizr, neither of which
    have any particular use to me), and other assorted bits.

    The belief is that when one is to build an "HTML 5" website
    (fallacy alert: appeal to popularity) that one must utilize
    "HTML 5 Boilerplate". Why? Simply because the "popular" developers
    are using it. Supposedly, it also has countless hours of research
    behind it. Anyone who has read this group for an extended period
    of time knows that "research" isn't always indicative of code
    quality (hat tip to jQuery, YUI, Dojo, et al).

    Since HTML 5 *still* doesn't have sufficient support (hopes
    are with IE 10) to be considered "implemented", one has to
    wonder just how a massive amalgam of resources can make websites
    "just work". At least in this case, users have a decision to trash
    what they consider unnecessary (unlike jQuery).

    For the majority of users, this is simply overkill.
    Ritualistically including code hinders the learning process
    involved with programming. There's little potential to improve
    one's knowledge of a topic by slapping on a copy-pasted
    solution, especially if it's poorly written (which is common).

    The biggest blow is marketing. Take a gander at
    "HTML 5 Boilerplate"'s website (won't link). It's chock full of
    over-the-top marketing speak. The first time I read it, I was
    heavily offended by the textual content. I seriously pondered
    if it was written by a prepubescent.

    Web development is in vogue. People want to create "cool"
    projects, but are dismayed by the amount of effort and research
    required to create something meaningful. The solution? Abdicate
    responsibility to those who have already "done the research".
    That's why "frameworks" and "libraries" have sprung up like
    weeds in the past half-decade or so.

    It is by this type of behavior that "cargo cult programming"
    is characterized. My opinion is that it's one of the prime evils
    currently in web development (along with overconfidence).
     
    Matt McDonald, Nov 10, 2011
    #5
  6. It is not new, but it is much more likely when people do not
    understand the details. Sometimes, it is something that works, but it
    is not known why. Such a thing then tends to be dragged along into
    other areas where it may not be relevant.

    Sincerely,

    Gene Wirchenko
     
    Gene Wirchenko, Nov 10, 2011
    #6
  7. David Mark

    Evertjan. Guest

    Ant wrote on 10 nov 2011 in comp.lang.javascript:
    If it is ".. seems to serve .." it is a perfect definition of DNA.
     
    Evertjan., Nov 10, 2011
    #7
  8. On Fri, 11 Nov 2011 00:36:57 -0000, "Richard Cornford"

    [snip]
    I got 5500.
    Also, later programmers might not know and think, "I do not know
    what it does, but removing it might break the code." Another
    possibility is that it might be thought to work that way with old
    versions of the language.

    At the point I am in JavaScript, I would not catch the error. I
    might eventually since any code that I do not understand tends to
    stick my attention. But I might not have time to get to it.
    A beautiful example of ugliness.

    [snip]

    Sincerely,

    Gene Wirchenko
     
    Gene Wirchenko, Nov 11, 2011
    #8
  9. It's growing! It's growing!

    :)-)

    Hans-Georg
     
    Hans-Georg Michna, Nov 11, 2011
    #9
  10. David Mark

    David Mark Guest

    Yes, these things are all about marketing, which is ironic considering
    that they are the first ones to cry (and I do mean cry) carpetbagger
    at the first sign of criticism. How many times have I heard "you are
    just jealous of our library" or "you are just trashing our library to
    promote yours" out of the mouths of (seemingly) petulant tweens? Of
    course, many of them turn out to be adults whose brains have been
    destroyed by too many television commercials. They'll take warm,
    glossy ad copy over cold, dull facts any day. Anybody trying to
    "sell" them with reasoned arguments is just trying to make them look
    stupid. They'd apparently much rather be made fools of daily by
    disingenuous marketers. ;)

    The face of Javascript libraries is a pinched, frustrated visage,
    spewing hatred and PKB insinuations at all who dare to question
    "established" best practices. From the history of these things, it's
    clear they are very frustrated; but when their users join in blaming
    the "self-promoting" messenger, you know you are dealing with a cult.
    Yes, and when it comes to JS, they all look alike to me (probably
    because they are constantly copying each other). Bad is bad, no
    matter how "cool" these things may seem to overwhelmed beginners.
    Unfortunately, it is relatively easy to write scripts that seem to
    work in the developers' array of browsers/configurations. This leads
    to the next stage, which is overconfidence.
    My thoughts exactly.
     
    David Mark, Nov 11, 2011
    #10
  11. Is it worth the time? It can be rather time-consuming to track
    such things down. Cost-benefit analysis may lead to "Meh. Why
    bother?"
    No. Even if I had very polished code, I might not even do it
    even then.
    Oh, sure. Bring logic into it!
    Extra verbiage that does nothing useful is ugly by my sense of
    aesthetics.

    Yes to your second sentence. A favourite quote of mine: "There
    are two ways of constructing a software design: One way is to make it
    so simple that there are obviously no deficiencies, and the other way
    is to make it so complicated that there are no obvious deficiencies.
    The first method is far more difficult." -- C.A.R. Hoare

    Sincerely,

    Gene Wirchenko
     
    Gene Wirchenko, Nov 11, 2011
    #11
  12. In comp.lang.javascript message <bd12b66f-33f5-43d2-8e22-6f81b42c3d8b@n1
    4g2000vbn.googlegroups.com>, Thu, 10 Nov 2011 06:09:29, David Mark
    It could be useful if there were, linked from within each posted Tip, a
    Web Index of Tips, linking to Web copies.

    One Tip might be the answer to "I have an on-screen element, of known
    size, with an on[dbl]click method; how do I obtain the co-ordinates of a
    [double-]click with respect to a given position in the element itself?"

    The aforementioned index page could have a feedback form, partly for
    suggesting topics for tips and possible answers. The latter, when
    imperfect, would provide fodder for further Tips, of course.

    In what appears to be the cinsoft home page, "By" should be "To".

    Consider a page, <http://www.cinsoft.net/position.html>, which has been
    found by a serendipitous Google search for "starting point for a move
    animation" or otherwise. It recommends
    o = getElementPositionStyles(el);
    - and that does not work. The necessary further information is not
    clearly provided; indeed, the page seems to have no <a href="...">Home
    Page</a>.
     
    Dr J R Stockton, Nov 11, 2011
    #12
  13. Nice! I just got 19,000+ hits. Strange that it varies so much.

    Anyway, do you have more examples? These would be good demos of
    the general ignorance towards correct JavaScript programming.
    Good for demos.

    Hans-Georg
     
    Hans-Georg Michna, Nov 12, 2011
    #13
  14. David Mark

    Eric Bednarz Guest

    I’m getting the impression that you think that the purpose of splitting
    and concatenating the generic identifier is somehow related to escaping
    the ETAGO delimiter (‘</’), while it is much more likely to be related
    to Norton ‘Internet Security’ inserting it’s dreaded SymError function
    after the first instance of anything that looks like the start tag of a
    script element (and ususally messing things up in the process).
     
    Eric Bednarz, Nov 12, 2011
    #14
  15. Suppose that was the case, then it would be a Bad Idea to work around that.
    Either it is a bug in Norton InSecurity, then working around it will help to
    keep it forever, having everyone to jump forever through the hoops that
    Symantec's incompetent, greedy developers once set up. Or it is a feature,
    then one would ignore the user's wishes, which is always a bad idea.


    PointedEars
     
    Thomas 'PointedEars' Lahn, Nov 12, 2011
    #15
  16. David Mark

    J.R. Guest

    Considering an element having scrollbars, we might use:

    return [el.scrollTop + el.clientHeight,
    el.scrollLeft + el.clientWidth];

    Note: scrollWidth/scrollHeight are buggy in IE6-8 and Opera 10. So, we'd
    need to compare (scrollTop + clientHeight) to scrollHeight too. In IE8,
    scrollWidth is 5 pixels off. See
    <http://www.quirksmode.org/dom/w3c_cssom.html>
     
    J.R., Nov 13, 2011
    #16
  17. Please explain what the scroll position has to do with the element
    dimensions.
    That states that *scrollHeight* is buggy in IE *5.5* to 7, and that
    scrollWidth is _correct_ in those versions. It also states that scrollWidth
    is 5 pixel off in IE 8, and that "Opera gives odd, incorrect values." Quite
    different from what you stated.


    PointedEars
     
    Thomas 'PointedEars' Lahn, Nov 13, 2011
    #17
  18. And I got alternatingly 2,900 or 2,667.

    It's not that strange that it varies. The common Google search is known
    to give varying results depending on Google server used (e.g.
    www.google.com vs. www.google.de), language settings, Google
    customization options, cookies (carrying e.g. information about past
    searches), browser and the request headers it sends, and possibly phase
    of the moon. There is most probably intentional randomization, too, due
    to Google testing new functionality or just carrying out A/B testing or
    something similar.

    Google code search may have any of these causes of variation, and maybe
    some more too.

    Moreover, the figures that Google tells us are, well, just figures it
    tells us. They probably reflect some internal hit counts. But if you try
    scan through the results, clicking on the last of the results page
    number in the list at the bottom, you may observe that the hit count
    drops at some point. In my test in this case, page 19 said
    "Results 181 - 190 of 2,665"
    but clicking on "Next" led to
    "Your search - typeof\s*\(?\s*[\S]+\s*\)?\s*(!|=)==?\s*("|')array("|')
    lang:javascript - did not match any documents."

    In the common Google search, it seems that at some point, Google set an
    upper limit of 999 on the number of hits it actually shows to the user.
    That is, when the user proceeds from one hit page to the next, or takes
    a shortcut via the page number links, Google stops showing results at
    some point, apparently so that in no circumstances can you go past
    result 999. So the count that it initially gives can be just about
    anything. I don't think it's completely arbitrary, but it surely isn't a
    reliable count of anything.
     
    Jukka K. Korpela, Nov 13, 2011
    #18
  19. David Mark

    J.R. Guest

    The clientWidth and clientHeight properties return the width and height
    of the content field, excluding borders and *scrollbars* (when visible),
    but including padding. If the element's content overflows then the
    browser might show scrollbars depending on the overflow CSS property. In
    the latter case, clientWidth and clientHeight won't retrieve the
    measurement of the hidden parts of the element's content.

    A better approach would be using scrollWidth and scrollHeight, but these
    properties are "buggy" in IE and Opera, according to
    <http://www.quirksmode.org/dom/w3c_cssom.html#elementview>. The irony
    here is that scrollWidth and scrollHeight were introduced by Microsoft
    in their MSIE's DHTML object model...

    Therefore, if we want a "getInnerDimensions" function to return the
    correct dimensions for the element's content we will need to add the
    scrollTop and scrollLeft values to the clientHeight and clientWidth
    values respectively, otherwise we would not get the correct inner
    dimensions if the element is scrolled all the way down or to the right.

    But what if the element doesn't have scrollbars? No problem, because
    scrollTop and scrollLeft will return zero.
    I don't think it is *quite* different, perhaps a *little* different...
    In fact, it is better to avoid scrollWidth/scrollHeight whenever we need
    a cross-browser code.
     
    J.R., Nov 13, 2011
    #19
  20. And if I scroll down or right a scrollable element its content becomes
    higher/wider?
    It is better to be aware of it and use *sensible* workarounds if necessary.
    This is actually one case where property inference (window.opera) is useful
    and acceptable; IE should be dealt with using Conditional Comments instead.


    PointedEars
     
    Thomas 'PointedEars' Lahn, Nov 13, 2011
    #20
    1. Advertisements

Ask a Question

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

You'll need to choose a username for the site, which only take a couple of moments (here). After that, you can post your question and our members will help you out.