How to set WebView height to the height of HTML content in React Native

How to set WebView height to the height of HTML content in React Native
cto of flexwork. digital architect, interested in artifical intelligence and machine learning. working anywhere.

We are working on a project where we had to display some HTML content inside a react-native webview.

React-native offers a component called WebView, this component enable applications to display html either from a URL or from a raw HTML using the property source.

source={{uri:http://flexwork.io}} or source={{html:rawhtml}}

The webview offers many nice built in feature, the down side that you have to set it with a fix height and if the html content height is bigger than the fixed height, webview will display a scrolling bar which is not very nice from Ui/UX point of view.

So the task was to find a way how to set the height of the webview to exactly the height of the html content. Since it was little difficult to find the work around, we wanted to share with you the solution so here we go:

Find a way to communicate with the webview
1) First task was to find a way to get the height of the document from the HTML inside the webview to the react-native component.Lucky for us, WebView component offers a callback method called onNavigationStateChange this method fire each time the webview start to load or end loading and returns an event as a parameter that contains the URL and the document title along with other useful information.

So if we can somehow set the title of the document to the height of the html document, we can later extract it from the event arg in the  onNavigationStateChange callback, convert it to a number and set it to the height of webview.

So the onNavigationStateChange will look like this

//the handling method of the event onNavigationChange
onNavigationChange(event) {
        if (event.title) {
            const htmlHeight = Number(event.title) //convert to number
            this.setState({Height:htmlHeight});
        }

     }
//WebView component inside the render method 
<WebView scrollEnabled={false}
                  source={{html:rawHtml}} 
                  style={{height:this.state.Height)}}
                  onNavigationStateChange={this.onNavigationStateChange.bind(this)}>
</WebView>

2) Set document title to the height of the html content.

Now we need to write some javascript that will calculate the height of the html content set it to the document.title and send it to the webview.

First we used document.height but this property is deprecated and not working in most of the browsers. So we used document.body.clientHeight and document.documentElement.clientHeight.

if(document.documentElement.clientHeight>document.body.clientHeight)
{
  document.title = document.documentElement.clientHeight
}
  else
{
  document.title=document.body.clientHeight
};

But for small html content we got some white space so we had to find another way to calculate the exact height of the html. So we had to wrap all the content of the html in a <div> and calculate the height of that <div> element using the clientHeight property.

<script>
window.location.hash = 1;
 var calculator = document.createElement("div");
 calculator.id = "height-calculator";
 while (document.body.firstChild) {
    calculator.appendChild(document.body.firstChild);
 }
 document.body.appendChild(calculator);
 document.title = calculator.clientHeight;
</script>

and also we need to set some style so we can get the exact height.

<style>
body, html, #height-calculator {
    margin: 0;
    padding: 0;
}
#height-calculator {
    position: absolute;
    top: 0;
    left: 0;
    right: 0;
}
</style>

Finally we inject the javascript code and the css style with the rawHtml. We used a property called injectJavaScript of the webview component but for some raison, it didn’t worked well on Android. So we had to inject those scripts with the rawHtml.

<WebView scrollEnabled={false}
                  source={{html:rawHtml+style+script}} 
                  style={{height:this.state.Height)}}
      javaScriptEnabled ={true}
      scrollEnabled={false}
      onNavigationStateChange={this.onNavigationStateChange.bind(this)}>
</WebView>

So here we go, that solution worked on both android and IOS, we got inspired by many solutions on the internet that didn’t work perfectly.

You can see a small demo here

Also published on Medium.