Updated 2014-01-20 13:05:19 by RLE

Using JavaScript to update Dynamic Documents

Description  edit

A question came up on comp.lang.tcl about a CGI which gave continuous updates to a Netscape browser. The original question had to do with a problem where Netscape stayed busy even after the CGI was finished (which was about two hours(!) after its started). This evolved into a discussion of how to get page updates that would mimic a real-time monitor without resorting to the use of Java.

JavaScript provides a way to modify the contents of a DHTML page, and it occured to me that by sending small JavaScript segments to the browser, it might be possible to have it appear like a real-time monitor that could add or update sections of page rather than simply append to a page.

I've written a couple of proof-of-concept pages that do this via PHP, but any CGI-like backend will work. What counts is the contents of the JavaScript and the browser's DOM. You can see the working examples and the PHP code which produced them at http://www.rlenter.com/stuff/server-monitor.html. The critical part seems to be

  1. Build your entire page, up to but not including the terminating </body> tag. [*]
  2. Identify the insertion/update point in JavaScript either via a position index (e.g., it's the fourth paragraph) or an id=ID-NAME tag.
  3. Output the JavaScript to insert into/update the page as updates become available.
  4. You need to be able to flush output from the server. Browsers may not update the page if they do not receive a full line, so be sure to include a terminating newline in your output.

[*] BR: Actually in my experience, you can also send scripts after the closing </body> and </html> and the browser will evaluate it.

Here is a complete example which replaces the second paragraph (identified via the id=x1 tag) with new information which is then updated periodically. You can view the page via http://www.rlenter.com/stuff/server-mon2.html.
<?php                                   // -*- php -*-

$header = '
<!DOCTYPE html PUBLIC "-//W3C//DTD xhtml 1.0 Transitional//EN"
                "http://www.w3.org/TR/REC-html40/loose.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<title>JavaScript Monitor Test</title>
</head>

<body>
<h1>JavaScript Monitor Test</h1>

<p> This is a test of a JavaScript Monitor which feeds additional
information to the page in "real-time" and eventually finishes the
page.    This particular "monitor" writes out the page then the
replaces a paragraph with updated information using JavaScript.  If
you look at the page source in your browser, you will see the
original HTML source plus all of the JavaScript commands.  The
"trick" here is that the JavaScript parts which update the page come
out slowly.  <a href="server-mon2.phps">Click here</a> to see the
PHP source for this page.</p>

<p id ="x1">Replace me.</p>

<hr />
<address>
Roland B. Roberts, PhD<br />
RL Enterprises
</address>
</body>
';

$trailer = '</html>';

echo eval ('?> ' . $header . '<?php ');

echo '<script language="JavaScript" type="text/javascript">' . "\n";
echo '    myP=document.getElementById("x1");' . "\n";
echo '    myTextNode=myP.childNodes[0];' . "\n";
echo '</script>' . "\n";
flush ();
for ($i = 1; $i <= 10; $i++) {
   sleep (2);
   echo '<script>' . "\n";
   if ($i == 1) {
      echo '    myTextNode.data = "' . "$i" . '";' . "\n";
   } else {
      echo '    myTextNode.data = myTextNode.data + "' . "..$i" . '";' . "\n";
   }
   echo '</script>' . "\n";
   flush ();
}

echo $trailer;
?>

See Also  edit

DYNAPI
a cross-browser javascript library used to create Dynamic HTML components on a web page
Ajax

Still to do: identify the versions of DOM, JavaScript, and CSS that make this possible, and correlate them with delivered browser versions. Also, CL is preparing demonstrations written in (server-side) Tcl [1], and working on others which update images rather than text.

[CL needs to explain some of the applications of the technique.]

Examples currently work with: IE 5.5 and 6, Netscape 6, Mozilla 0.9.5, ...

Examples do not work with: Netscape 4, Opera 6, ...

Arjen Markus: I have been experimenting with something akin to this - using the browser as a device for "on-line visualisation". That is, a long-running computation produces an output file, a plotting program produces a GIF-file from the last record in the output file and the browser regularly checks if there is a new picture. It is not fool-proof as yet, but the principle seems to work.

CL responds that, yes, there are both browser-pull and server-push applications of the technique. Its kernel, to my mind, has to do with DOM navigation or computation. A few more comments appear in [2].

[matt.donohue@necsam.com]: This is a cool idea. I have been working with a similar concept to update a controller page with small agent.php windows that send data back to a controller page while running in the background. I made a mini test using straight html: http://www.nectechmedia.com/auct.sample/controller.htm

still buggy but, works- Matt

BR: One gotcha that I remember is that some (all?) browsers assume a server timeout after something like 5 minutes even if they get new data all the time.

Another way of achieving a similar effect (and without JavaScript) is a refresh instruction in the HTML like
<meta http-equiv="refresh" content="10">

With that you have to re-create the whole page, though.

Stu 2007-10-23: I think this is the right page for Using a Web Browser as a Backend Javascript Processor

Run this script, run a browser and point it at the server (most likely http://localhost:12345). Type javascript to evaluate in the ''JS' box, set the var result to whatever result you want back and click Send.

# Use web browser as javascript backend.
# October 2007
# Stuart Cassoff

proc srv {chan addr port} {
    fconfigure $chan -translation auto -buffering line -blocking 0
    fileevent $chan r [list plop $chan]
}

proc plop {c} {
    global currchan
    set line [gets $c]
    if {[lindex $line 1] eq {/favicon.ico}} { close $c; return }
    while {[gets $c l] > 0} {}
    puts $c {HTTP/1.0 200 OK}
    puts $c {Content-Type: text/html}
    puts $c {}
    if {[lindex $line 1] eq {/}} {
        set currchan $c
    }  else {
        set a [string range [lindex [split [lindex $line 1] &] 1] 2 end]
        .tr insert end >>>\ [string map {%20 { }} [string range [lindex [split [lindex $line 1] &] 1] 2 end]]\n
        .tr see end
    }
    .lc configure -text "Connected"
}
proc sendjs {} {
    global currchan
    set o {<script type="application/javascript">var result='';try}
    append o "\{"
    append o [.tj get 1.0 end]
    append o "\}"
    append o {catch (e)}
    append o "\{"
    append o {result = e}
    append o "\}"
    append o {document.location.href='result&r=' + result.toString();</script>}
    puts $currchan $o
    close $currchan
    .lc configure -text "Not Connected"
}

grid [label .lj -text JS] [text .tj -width 60 -height 5]
grid [button .bj -text Clear -command ".tj delete 1.0 end"] ^
grid [label .lr -text Result] [text .tr -width 60 -height 5]
grid [button .br -text Clear -command ".tr delete 1.0 end"] ^
grid [button .bs -text Send -command sendjs] [label .lc -text "Not Connected"]
bind . <Escape> exit

socket -server srv 12345

# EOF