13

I've got a javascript function 'doAll', which is supposed to write all the text that was put between <p>, </p> tags. In my code it is used in two ways.

It works correctly when I just call it at the end of the page. However it should also be triggered out when click-button event occurs. For some reason it doesn't do what it is supposed to do this time. When one click on the button it just prints the inner html of the very first <p>...</p> html element.

My question is why it works differently in these two situations and what should I do to fix this problem.

<!DOCTYPE html>
<html>
    <head>
        <script>
            function returnList(x)
            {
                return document.getElementsByTagName(x);
            }

            function writeList(x)
            {
                var i = 0;

                while (x[i])
                {
                    document.write(x[i++].innerHTML + '<br/>');
                }
            }

            function doAll(x) 
            {
                writeList(returnList(x));
            }
        </script>
    </head>

    <body>
        <p>XXX</p>
        <p>YYY</p>
        <div style = 'background-color: gray;'>
            <p>YYY</p>
            <p>XXX</p>
        </div>

        <button type = 'button' onclick = "doAll('p')">
            Click me!
        </button>

        <script>
            document.write('<br/>');
            doAll('p');
        </script>
    </body>
</html>

Here is the 'output':
XXX
YYY
YYY
XXX

XXX
YYY
YYY
XXX

And here is what one can see after clicking the button:
XXX

Hubert Siwkin
  • 385
  • 3
  • 16
  • Why does the first output consist of 8 lines although there are only 4 p-tags? that looks like the on click is run to early. – Lars Ebert Jul 15 '13 at 13:25
  • Remember: `debugger` + browser developer tools are always your friends. – DontVoteMeDown Jul 15 '13 at 13:44
  • @LarsEbert: nope, it's not because of the click button. Four additional lines of output are the consequence of six last lines of my code :D. As you can see 'doAll' function is triggered automatically by the section on the bottom of the code. – Hubert Siwkin Jul 15 '13 at 20:39
  • 1
    Since my "answer" got deleted, for the right reasons, I'll post this as a comment. I stand my ground when saying that JavaScript isn't meant to be written in C-like style. ASI is one problem that can occur unexpectedly. If you want to validate the 'correctness' of your JavaScript code, run it through the JSLint.org validator. The better you score there, the less likely unexpected bugs will pop up. ASI Source: http://stackoverflow.com/questions/2846283/what-are-the-rules-for-javascripts-automatic-semicolon-insertion-asi JSLint: http://www.jslint.com/ – Arninja Jul 16 '13 at 08:12
  • @Arninja: Thanks :D It was a useful post :) – Hubert Siwkin Jul 16 '13 at 20:34

3 Answers3

14

The problem is that the page is already rendered, when you click your button. Triggering document.write() at this point will override the whole document. It writes the first element, because this is the only element that still exists, when you triggered your function. Any other element will simply not be there after document.write() executed for the first time.

Even your function itself will not be part of the document anymore. If you MUST work with document.write() or you just want to use it for testing reasons, you have to somehow "cache" your data and output it at once:

function writeList(x) {
    var i = 0, data = "";

    while (x[i]) {
        data += x[i++].innerHTML + '<br/>';
    }

    document.write(data);
}
Amberlamps
  • 39,180
  • 5
  • 43
  • 53
3

document.write is able to write content only to a document which is being currently loaded. Once loaded, it behaves a little bit differently - see https://developer.mozilla.org/en-US/docs/Web/API/document.write . Use special container element and innerHTML to write your desired output.

function writeList(list) {
    var i, result = "";
    for (i = 0; i < list.length; i++) {
        result += list[i].innerHTML + "<br />";
    }
    document.getElementById("myContainer").innerHTML = result;
}

http://jsfiddle.net/3yPAW/1/

Pavel Horal
  • 17,782
  • 3
  • 65
  • 89
2

The other two answers explained why, I'll suggest you how to avoid this:

append your HTML to innerHTML of the body (or another element);

instead of

document.write(x[i++].innerHTML + '<br/>');

use

document.getElementsByTagName("body")[0].innerHTML += x[i++].innerHTML + '<br/>';

Demo: http://jsfiddle.net/MNCbx/

Andrea Ligios
  • 49,480
  • 26
  • 114
  • 243