13

I'm getting html content from an api and I want to render it as a web view in my application.

The webview component is nested in a scrollview because it has some other contents above my render function is something like this:

render() {
      var html = '<!DOCTYPE html><html><body>' + this.state.pushEvent.Description + '</body></html>';

    return (
      <ScrollView>
        <View style={styles.recipe}>
          <Image source={{uri: this.state.pushEvent.ImageUrl}}
            style={styles.imgFull} />
          <Text style={styles.title}>{this.state.pushEvent.Title}</Text>
          
          <WebView style={{padding: 20}}
            automaticallyAdjustContentInsets={false}
            scrollEnabled={false}
            html={html}>
          </WebView>
        </View>
      </ScrollView>
    );
  }

The problem is that my html is just 20pt high that is my padding. Is there a clever way to get the height of the content?

daniele bertella
  • 695
  • 1
  • 7
  • 14
  • I've used @hedgerwang hack to let my WebView comunicate via document.title its height, here is the [link](https://github.com/facebook/react-native/issues/586#issuecomment-90826117) but there should be a better way without installing external npm – daniele bertella Oct 06 '15 at 09:34
  • I answered solution on this link: https://stackoverflow.com/questions/35446209/react-native-webview-height/55889904#55889904 Hope this help. – Tung Nguyen Apr 28 '19 at 11:48

5 Answers5

10

As I wrote on the comment above this is what I came out with, but I'm still thinking that there should be a better solution.

Here is the issue on github I took inspiration from: @hedgerwang answer

In my var html I added a simple script just before the closing body tag, I simply save the height of my html in my document title:

<script>window.location.hash = 1;document.title = document.height;</script>

then I added onNavigationStateChange props in my WebView component with a callback that set a state "height" variable and then set this height to my WebView. As I said, that did the trick, with just a little flahsing while changing the content in the WebView, but I think it's a dirty hack.

At the end I decided to change the api to have something that I don't have to include in a WebView

But maybe this can help, here the full code.

onNavigationStateChange(navState) {
  this.setState({
    height: navState.title,
  });
}  
render() {
  var html = '<!DOCTYPE html><html><body>' + this.state.pushEvent.Description + '<script>window.location.hash = 1;document.title = document.height;</script></body></html>';
return (
  <ScrollView>
    <View style={styles.recipe}>
      <Image source={{uri: this.state.pushEvent.ImageUrl}}
        style={styles.imgFull} />
      <Text style={styles.title}>{this.state.pushEvent.Title}</Text>

      <WebView style={{padding: 20}}
        automaticallyAdjustContentInsets={false}
        scrollEnabled={false}
        onNavigationStateChange={this.onNavigationStateChange.bind(this)}
        html={html}>
      </WebView>
    </View>
  </ScrollView>
);
}
daniele bertella
  • 695
  • 1
  • 7
  • 14
  • 2
    Thanks. I ended up implementing it like this https://gist.github.com/epeli/10c77c1710dd137a1335 This version will also handle images but still fails for any other dynamic content. – esamatti Jan 13 '16 at 13:04
  • This seems to no longer work (RN 0.28) -- like the comments to Daniel's answer below indicate (injecting JavaScript), the navState.title attribute now seems to be ridiculously large...for one line of text, the value is 568 for me... – user Jun 22 '16 at 19:31
  • This doesn't work in React Native Version 0.30 Height is always 0. And I think it should change the import something like this. import React, { Component } from 'react'; import {WebView, View, Text} from "react-native"; – Georgi Kovachev Jul 31 '16 at 09:51
  • Sorry, This code is working fine, it was my mistake, I should add the script code just before

    but I added it just after

    . Thanks
    – Georgi Kovachev Jul 31 '16 at 10:30
  • 1
    The calculated height seems to be wrong in Android - The bigger the page the more it is wrong. Any ideas how to rectify? – Gant Laborde Sep 13 '16 at 22:57
  • 1
    This doesn't work on Android when keyboard is showing. Any idea of fix? – Georgi Kovachev Dec 04 '16 at 20:55
  • confirmed, this is not working on Android, only in iOS, any idea? – Dante Cervantes Nov 27 '17 at 21:14
  • 3
    If you're trying to implement this now, note that document.height is obsolete. Instead use document.body.clientHeight. – Surendra Pathak Feb 14 '19 at 10:49
  • I am getting 0 always when using `document.body.clientHeight` – Zeeshan Ahmad Khalil Oct 08 '22 at 09:07
  • also, try checking `document.body.scrollHeight` as suggested [here](https://stackoverflow.com/a/44077777/9108471) because it worked for me – Zeeshan Ahmad Khalil Oct 08 '22 at 09:34
10

There is a better way to do it: you can inject custom JS with the injectedJavaScript property. The result of the evaluation will end up in the event object you receive in your onNavigationStateChange. This way you won't have to modify the HTML itself or hijack the title property.

See https://gist.github.com/dbasedow/ed6e099823cb8d5ab30e for an example

Daniel Basedow
  • 13,048
  • 2
  • 32
  • 30
0

This component worked perfectly for me. It adjusts the webview to fit the content and returns the height of the rendered html.

https://gist.github.com/epeli/10c77c1710dd137a1335

And change

import React, {WebView, View, Text} from "react-native";

for

import React from "react";
import {WebView, View, Text} from "react-native";

at the top of the file.

Agu Dondo
  • 12,638
  • 7
  • 57
  • 68
0

The only reliable way for doing this is through postMessage, in the injectedJavascript prop (just as in this component.

Pass document.scrollHeight in there and you're all set.

const jsString = `
  function post () {
    postMessage(
      Math.max(document.documentElement.clientHeight, document.documentElement.scrollHeight, document.body.clientHeight, document.body.scrollHeight)
    );
  }
  setTimeout(post, 200);
// other custom js you may want
`


// component class:
  _onMessage = (e) => {
    this.setState({
      webviewHeight: parseInt(e.nativeEvent.data)
    });
  }
  render() {
    return (
      <WebView
        source={yourURL}

        injectedJavascript={jsString}
        onMessage={this._onMessage}

        style={{ height: webviewHeight }}
      />
    )
  }
kriskate
  • 177
  • 2
  • 2
0

You can use HandleHTMLDimensionsFeature provided by @formidable-webview/webshell library. It will use injectedJavaScript to run a script in the WebView. This is how you would use it:

import React from 'react';
import makeWebshell, {
  HandleHTMLDimensionsFeature
} from '@formidable-webview/webshell';
import WebView from 'react-native-webview';

const Webshell = makeWebshell(
  WebView,
  new HandleHTMLDimensionsFeature()
);

export default function AugmentedWebview(props) {
  const onDocumentDimensions = React.useCallback(
    (dimensions) => {
      console.info(dimensions.content);
    },
    []
  );
  return (
    <Webshell
      onDOMHTMLDimensions={onDocumentDimensions}
      {...props}
    />
  );
}

The script run in the Web environement is available here. The dimensions will be sent each time the document size changes, dynamically. The script will use the best API available in the WebView, by order of preference, ResizeObserver, MutationObserver and finally, polling on a regular interval. If you are looking for an autoheight WebView, check out this guide.

Jules Sam. Randolph
  • 3,610
  • 2
  • 31
  • 50