Path: csiph.com!x330-a1.tempe.blueboxinc.net!usenet.pasdenom.info!weretis.net!feeder4.news.weretis.net!newsreader4.netcologne.de!news.netcologne.de!newsfeed.arcor.de!newsspool4.arcor-online.net!news.arcor.de.POSTED!not-for-mail Content-Type: text/plain; charset="UTF-8" Message-ID: <3002143.SPkdTlGXAF@PointedEars.de> From: Thomas 'PointedEars' Lahn Reply-To: Thomas 'PointedEars' Lahn Organization: PointedEars Software (PES) Date: Sat, 12 Nov 2011 13:32:16 +0100 User-Agent: KNode/4.4.11 Content-Transfer-Encoding: 8Bit Subject: Re: newbie Javascript checked is null or not an object Newsgroups: comp.lang.javascript References: <7a88bf5f-7821-49ca-85a8-bca655c28a36@u9g2000vbx.googlegroups.com> <4ebab172$0$14430$426a34cc@news.free.fr> <4ebb0ac7$0$28592$a8266bb1@newsreader.readnews.com> <1675485.FNyZMGkNft@PointedEars.de> Followup-To: comp.lang.javascript MIME-Version: 1.0 Lines: 265 NNTP-Posting-Date: 12 Nov 2011 13:32:17 CET NNTP-Posting-Host: c56975e9.newsspool3.arcor-online.net X-Trace: DXC=D^SBP@efGeKlU`@c^jLCbJMcF=Q^Z^V3H4Fo<]lROoRA8kF Denis McMahon wrote: >> var ff=document.form1.elements[elemnum]; // untested >> >> /* then wrap the tests so that it only looks for the checked property on >> actual checkboxes */ >> >> if (ff.type == "checkbox") ff.checked = !ff.checked; // untested > > […] > However, this code should never be applied to objects that do not have a > `checked' property in the first place, which renders the question moot and > the test unnecessary. > >> You could even (and I'm sure someone will shortly post saying why this is >> bad, > > It is bad, > >> but probably not actually explaining (a) why > > because it is comparably inefficient, incompatible, and unnecessary. Now as to the why. Remember, the suggested alternative was (formatted): var inputsInListcarsDiv = document.getElementById("listcars").getElementsByTagName("input"); for (var i = 0; i < inputsInListcarsDiv.length; i++) { if (inputsInListcarsDiv[i].type == "checkbox") { inputsInListcarsDiv[i].checked = !inputsInListcarsDiv[i].checked; } } Efficience ----------- The suggested alternative is comparably inefficient because it requires a lot more property lookups (P), calls (C) and operations (O). In order of execution: P: document P: document.getElementById C: document.getElementById("listcars") P: document.getElementById("listcars").getElementsByTagName C: document.getElementById("listcars").getElementsByTagName("input"); P: inputsInListcarsDiv O: inputsInListcarsDiv = document.getElementById("listcars").getElementsByTagName("input") `for' statement: P: i O: i = 0 inputsInListcarsDiv.length times (any case): P: i P: inputsInListcarsDiv P: inputsInListcarsDiv.length O: ToBoolean(i < inputsInListcarsDiv.length) P: inputsInListcarsDiv P: inputsInListcarsDiv[i] P: inputsInListcarsDiv[i].type O: inputsInListcarsDiv[i].type == "checkbox" inputsInListcarsDiv.length times (worst case): P: inputsInListcarsDiv P: inputsInListcarsDiv[i] P: inputsInListcarsDiv[i].checked O: !inputsInListcarsDiv[i].checked P: inputsInListcarsDiv P: inputsInListcarsDiv[i] P: inputsInListcarsDiv[i].checked O: inputsInListcarsDiv[i].checked = !inputsInListcarsDiv[i].checked inputsInListcarsDiv.length times (any case): P: i O: i++ It is a lot more efficient when written as follows: var inputsInListcarsDiv = document.getElementById("listcars").getElementsByTagName("input"); for (var i = inputsInListcarsDiv.length; i--;) { var elem = inputsInListcarsDiv[i]; if (elem.type == "checkbox") { elem.checked = !elem.checked; } } Then the following property lookups (P), calls (C) and operations (O) would take place, in order of execution: P: document P: document.getElementById C: document.getElementById("listcars") P: document.getElementById("listcars").getElementsByTagName C: document.getElementById("listcars").getElementsByTagName("input") P: inputsInListcarsDiv O: inputsInListcarsDiv = document.getElementById("listcars").getElementsByTagName("input") `for' statement: P: i P: inputsInListcarsDiv P: inputsInListcarsDiv.length O: i = inputsInListcarsDiv.length inputsInListcarsDiv.length times (any case): P: i O: ToBoolean(i--) P: inputsInListcarsDiv P: inputsInListcarsDiv[i] P: elem O: elem = inputsInListcarsDiv[i] P: elem.type O: elem.type == "checkbox" inputsInListcarsDiv.length times (worst case): P: elem P: elem.checked P: elem P: elem.checked O: !elem.checked O: elem.checked = !elem.checked It is still very inefficient by comparison because it involves - document.getElementById(), i.e. searching the entire document tree for one element (best case), and returning the reference to the corresponding element object, and - getElementsByTagName(), i.e. searching that element's subtree for descendant elements of a certain type (), building a live collection containing the corresponding element objects, and returning that collection. The `document.forms' collection, by comparison, is already a live collection of objects representing `form' elements, and its `elements' collection is already a live collection representing form controls of that particular `form' element. Therefore, the first approach requires only the following, in order of execution: P: document P: document.forms P: document.forms["form1"] P: document.forms["form1"].elements P: elemnum P: document.forms["form1"].elements[elemnum] [a lot more P's, and O's, and one C] However, it should be noted that none of those property accesses are necessary here. One can pass `this' from an event handler attribute of a control: function toggleAll(ctrl, name) { var checked = ctrl.checked; var checkboxes = ctrl.form.elements[name]; for (var i = checkboxes.length; i--;) { checkboxes[i].checked = checked; } } Which requires the following: C: toggleAll(this, 'foo') P: checked P: ctrl P: ctrl.checked O: checked = ctrl.checked P: ctrl P: ctrl.form P: ctrl.form.elements P: name P: ctrl.form.elements[name] O: checkboxes = ctrl.form.elements[name] `for' statement: P: i P: checkboxes P: checkboxes.length O: i = checkboxes.length checkboxes.length times: P: i O: ToBoolean(i--) P: checkboxes P: i P: checkboxes[i] P: checked O: checkboxes[i] = checked In any case, one can easily see from this that this kind of element object access must be more efficient (and cannot be less standards-compliant or less compatible, see below) than the suggested alternative. Compatibility -------------- The suggested alternative is comparably incompatible, because not only it relies on the availability of the document.getElementById() method, but also on that element objects have a getElementsByTagName() method. The document.getElementById(…) method was introduced with W3C DOM (HTML) Level 1 [1]. But in that Specification, the HTMLElement interface has no getElementsByTagName() method [2]. It was only introduced there with W3C DOM Level 2 Core [3]. By contrast, the `forms' and `elements' collections [4,5] date back to the so-called "DOM Level 0" [6] introduced by Netscape Navigator 3.0 [7,8] and Internet Explorer 3.0 [9,10]. As a consequence of that, they can be expected to be supported by any script-enabled HTML user agent. Unnecessary ------------ Because we already have the `forms' and `elements' live collections in either proprietary and standards-compliant implementation, it is not necessary to search the entire document tree for suitable elements. [x] done. >> or (b) the 'proper' way of doing it) [x] done. PointedEars _________ [1] [2] [3] [4] [5] [6] [7] [8] [9] [10] -- When all you know is jQuery, every problem looks $(olvable).