Skip to content

Instantly share code, notes, and snippets.

@xyos
Created July 31, 2016 23:08
Show Gist options
  • Save xyos/dcaa43fa561760b9d6194d78e5fb7721 to your computer and use it in GitHub Desktop.
Save xyos/dcaa43fa561760b9d6194d78e5fb7721 to your computer and use it in GitHub Desktop.
import {WebView, View} from "react-native";
import React, { Component } from 'react'
const BODY_TAG_PATTERN = /\<\/ *body\>/;
// Do not add any comments to this! It will break line breaks will removed for
// some weird reason.
var script = `
;(function() {
var wrapper = document.createElement("div");
wrapper.id = "height-wrapper";
while (document.body.firstChild) {
wrapper.appendChild(document.body.firstChild);
}
document.body.appendChild(wrapper);
var i = 0;
function updateHeight() {
document.title = wrapper.clientHeight;
window.location.hash = ++i;
}
updateHeight();
window.addEventListener("load", function() {
updateHeight();
setTimeout(updateHeight, 1000);
});
window.addEventListener("resize", updateHeight);
}());
`;
const style = `
<style>
body, html, #height-wrapper {
background-color: #232323;
color: #fff;
margin: 0;
padding: 0;
}
a, a:visited, a.hover {
color: #FFCC55;
}
#height-wrapper {
position: absolute;
top: 0;
left: 0;
right: 0;
}
</style>
<script>
${script}
</script>
`;
const codeInject = (html) => html.replace(BODY_TAG_PATTERN, style + "</body>");
/**
* Wrapped Webview which automatically sets the height according to the
* content. Scrolling is always disabled. Required when the Webview is embedded
* into a ScrollView with other components.
*
* Inspired by this SO answer http://stackoverflow.com/a/33012545
* */
export default class WebViewAutoHeight extends Component{
constructor(props) {
super(props)
this.state = {
realContentHeight: this.props.minHeight,
};
this.handleNavigationChange = this.handleNavigationChange.bind(this);
}
handleNavigationChange(navState) {
if (navState.title) {
const realContentHeight = parseInt(navState.title, 10) || 0; // turn NaN to 0
this.setState({realContentHeight});
}
if (typeof this.props.onNavigationStateChange === "function") {
this.props.onNavigationStateChange(navState);
}
}
render() {
const {source, style, minHeight, ...otherProps} = this.props;
const html = source.html;
if (!html) {
throw new Error("WebViewAutoHeight supports only source.html");
}
if (!BODY_TAG_PATTERN.test(html)) {
throw new Error("Cannot find </body> from: " + html);
}
return (
<View>
<WebView
{...otherProps}
source={{html: codeInject(html)}}
scrollEnabled={false}
style={[style, {height: Math.max(this.state.realContentHeight, minHeight)}]}
javaScriptEnabled
onNavigationStateChange={this.handleNavigationChange}
/>
</View>
);
}
};
WebViewAutoHeight.propTypes={
source: React.PropTypes.object.isRequired,
injectedJavaScript: React.PropTypes.string,
minHeight: React.PropTypes.number,
onNavigationStateChange: React.PropTypes.func,
style: WebView.propTypes.style,
};
WebViewAutoHeight.defaultProps = {
minHeight: 100
}
@lorenzsell
Copy link

This works for me. Can you possibly explain why doing all that manipulation with the js makes a difference?

@Aragorn450
Copy link

Aragorn450 commented Feb 8, 2019

With the release of Chrome 72 for Android on January 29, 2019, this solution has broken. In handleNavigationChange, navState.title is set to the full html of the page instead of the height number expected.

Switching to using messages like demonstrated here: https://stackoverflow.com/a/52556053/904125 seems to be working however. We are still testing with as many devices as possible to make sure it works in all instances, but so far it is working well.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment