Let’s say your app is consuming rich text content from an API. That content is returned via a get request and returns a response that is a plain-text string of HTML content. How would you display that content in your app? Rather than write a whole HTML parser, utilizing a WebView is an excellent choice but presents another problem: how can you size a WebView component dynamically to closely fit the variable content the app receives from the API. Here, I’ll outline the approach and one such solution to just this problem.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
The key part of this code is the injected javascript, which I’ll break down in detail:
injectedJavaScript={`
document.querySelectorAll('a').forEach(link => {
link.addEventListener('click', (event) => {
event.preventDefault();
window.ReactNativeWebView.postMessage(link.href);
});
});
setTimeout(function() {
window.ReactNativeWebView.postMessage(document.body.scrollHeight);
}, 100); // a slight delay seems to yield a more accurate value
`}
The first part of this injected javascript is a query selector to intercept and “click” events that occur on anchor tags within the embedded html, which allows us to handle these clicks outside of the actual webview later on. Following that, we include a single line of code that captures and transmits the scroll height of our little proto-webpage. For both these features, we use postMessage to hoist the result up to the parent WebView and receive those messages via the onMessage prop. Lastly, we take the value of scrollHeight and set that as the component height via a useState hook. In practice, here’s what that looks like:
Without setTimeout
With setTimeout
N.B – A former colleague of mine pointed out that by wrapping the onMessage call to report scrollHeight in a slight delay, we get a much more accurate value back – without the delay, there is a large block of extra space at the end of the of the formatted HTML (indicated by the line/divider in my demo). I suspect this is in part due to some additional styles within the component and injected into the WebView via my HTML_HEAD variable (an attempt to “normalize” the web content to more closely match the style of the app itself), but my attempts to mitigate that without a timeout have so far proven unsuccessful. Special thanks to Matthew Bennett for sharing a solution to this after stumbling across a Stack Overflow post.
Regardless, we now have a very close approximation of a variable height web content rendered in our app for consumption.
Full-time developer, part-time hobby-jogger, Tsar of awful check-in comments. I like cooking, exploring Chicago, and a good story. I write code sometimes.
View all posts by Joe Meyer