0

I am trying to make some modifications to an XHTML page using the outerHTML element, accessed via a Javascript function. The Javascript function that does this is called from an Android WebView like so:
1. Push a button, Javascript function is called which accesses outerHTML element, stores and, just for the sake of it, assigns it to another variable and replaces it like so: document.getElementById("bodyTag").outerHTML = newHTML

All of this works fine when the button is pressed the first time. Pressing it the second time results in the following LogCat error:

09-14 13:24:21.432: V/com.sriram.outerhtml.OuterHTML$2@4052e0f8(10931): Clicked click!
09-14 13:24:21.432: V/com.sriram.outerhtml.OuterHTML@40520da8(10931): Replacing outerHTML
09-14 13:24:21.452: D/com.sriram.outerhtml.OuterHTML$1@4052dcf8(10931): jswebview: Function to replace outerHTML.. with itself of level = LOG at line: 410
09-14 13:24:21.452: D/com.sriram.outerhtml.OuterHTML$1@4052dcf8(10931): jswebview: Uncaught TypeError: Cannot read property 'outerHTML' of null of level = ERROR at line: 411  

My questions:
1. The error indicates that the element with that tag is not available in the document. But that is not the case since it works the first time around. Where is the error?
2. How do I get this right?

The code:
OuterHTML.java:

/*
 * Each time the button is clicked, a javascript function is invoked.
 * Javascript function: Takes outerHTML, assigns it to new variable and replaces it.
 */

package com.sriram.outerhtml;

import java.io.File;

import android.app.Activity;
import android.os.Bundle;
import android.os.Environment;
import android.util.Log;
import android.view.Menu;
import android.view.View;
import android.webkit.ConsoleMessage;
import android.webkit.WebChromeClient;
import android.webkit.WebView;
import android.widget.Button;

public class OuterHTML extends Activity {

    private WebView wv;
    private Button click;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_outer_html);

        wv = (WebView) findViewById(R.id.webview);
        wv.getSettings().setJavaScriptEnabled(true);

        String path = Environment.getExternalStorageDirectory().getAbsolutePath() + "/JsWebView/";
        String path_file = "file:///" + path + "mypage_copy.xhtml";

        wv.loadUrl(path_file);

         wv.setWebChromeClient(new WebChromeClient() {

            @Override
            public boolean onConsoleMessage(ConsoleMessage cm) {
                /* 
                 * 1.Override console message to display messages from the javascript function.
                 */
                if(cm.messageLevel().equals("ERROR")) {
                    Log.d(this.toString(), "jswebview: " + cm.message() + " of level = " + cm.messageLevel() + " at line: " + cm.lineNumber());
                } else {
                    Log.d(this.toString(), "jswebview: " + cm.message() + " of level = " + cm.messageLevel() + " at line: " + cm.lineNumber());
                }
                return true;
            }
         });

        click = (Button) findViewById(R.id.click);
        click.setOnClickListener(new View.OnClickListener() {

            @Override
            public void onClick(View v) {
                Log.v(this.toString(), "Clicked click!");
                replaceOuterHTML();
            }
        });
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.outer_html, menu);
        return true;
    }

    public void replaceOuterHTML() {
        Log.v(this.toString(), "Replacing outerHTML");
        wv.loadUrl("javascript:replaceOuterHTML()");
    }
}

Layout:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context=".OuterHTML" >

    <Button
        android:id="@+id/click"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerHorizontal="true"
        android:layout_alignParentBottom="true"
        android:text="Click" />

    <WebView
        android:id="@+id/webview"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_above="@id/click"
        android:text="@string/hello_world" />

</RelativeLayout>

The XHTML page:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
 "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"> 
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<meta content="application/xml+xhtml;charset=UTF-8" />
<script src="highlightWordInSentence.js" type="text/javascript"></script>
<script src="jquery-2.0.2.min.js"> </script>
<title>Building your resume</title>
</head>
<body xmlns="http://www.w3.org/1999/xhtml" id="highlightbegin">
<h1>Building your resume</h1>
</body>  
</html>  

And the Javascript function:

function replaceOuterHTML() {

    console.info("Function to replace outerHTML.. with itself");
    var oldHTML = document.getElementById("highlightbegin").outerHTML;
    if(!oldHTML) {
        console.error("oldHTML is null.");
    } else {
        console.info("replacing oldHTML");
        var newHTML = oldHTML;
        document.getElementById("highlightbegin").outerHTML = newHTML;
    }
}
Sriram
  • 10,298
  • 21
  • 83
  • 136
  • Interesting. My original guess was that some functionality of the button, which gets initialised on load, is no longer there after replacing that part of the DOM tree (which doesn't cause another load event). But apparently the button is still there and it can still click, so that's not the problem. Not sure what to say now. I tried this with a static XHTML page (just your XHTML source with an added button), but that works; you can push the button as often as you want and the function runs normally. – Mr Lister Sep 15 '13 at 07:19
  • The problem is that I don't know what you're trying to do. Normal practice would be to leave the current DOM tree intact and just add something in one spot; not replace the entire body. But I guess that won't work for you? Question. Does this routine work if you use `innerHTML` instead of `outerHTML`? Or if you use `document.body` instead of `document.getElementById("highlightbegin")`? – Mr Lister Sep 15 '13 at 07:23
  • I am trying to search/highlight a specific instance of a word that is synchronized with audio (that speaks the text). In the complete function, I use other information like word before it etc. to zero in on the specific word to search. Once found, I use `span` tags to highlight/replace the word. This needs to be done in a loop (as long as the audio is running). If I use `innerHTML`, the `xmlns` attrib gets added automatically (see http://stackoverflow.com/questions/18766291/removing-xmlns-attributes-added-automatically). That is why I added the `xmlns` tag to the `body` and used `outerHTML`. – Sriram Sep 15 '13 at 09:49
  • You are right in saying that adding a button to the `xhtml` page does not result in any error no matter how many times the button is pressed. and in this example above, the error results only after the first such replacement. – Sriram Sep 15 '13 at 09:50
  • I tried using `innerHTML` and there was no problem irrespective of the number of times the button was clicked. When I print the output of `innerHTML`, I get the `xmlns` attribute added auto-magically. The function is supposed to replace the xhtml code. If I load the page into a browser and look at the source, the `xmlns` attributes are all missing. I am having difficulty as to what exactly is going on. – Sriram Sep 15 '13 at 10:51
  • But the xmlns attribute won't do any harm, as long as its value is the same as the value on the root element. I wouldn't lose any sleep over that. But I'm curious if replacing `document.getElementById("highlightbegin")` with `document.body` would make a difference. – Mr Lister Sep 15 '13 at 11:10
  • Hi - feel like revisiting and closing [this question](http://english.stackexchange.com/questions/48493/what-are-the-syllabification-rules-for-english) ? – mplungjan Sep 18 '13 at 08:09
  • I don't have an answer yet. Do you have one? – Sriram Sep 18 '13 at 09:40

0 Answers0