iou90 / react-native-autoheight-webview

An auto height webview for React Native
ISC License
492 stars 162 forks source link

Making WebView's autoheight work for web #226

Open Cogneter opened 2 years ago

Cogneter commented 2 years ago

So, over the past couple days, I've spent many hours trying to make my WebViews in React Native work in web (webpack). Thanks to this package and some elbow grease, I managed to make it work. Figured I'd leave instructions here for whoever encounters the same problems as me, to save them time.

My use case - displaying multiple WebViews on the screen (news posts with HTML markup) that need to have adaptable height (i.e. WebView height in React Native should match the post's content height). I'm using Expo with Ignite boilerplate for this project.

First things first - both react-native-webview and react-native-web-webview don't support automatic WebView height (see https://github.com/react-native-webview/react-native-webview/issues/413). You already know this, otherwise you wouldn't be browsing this package.

react-native-autoheight-webview does a good job of making automatic height work for Android and iOS, but it doesn't work for Web. The reason - react-native-web-webview does not define window.ReactNativeWebView, so the call window.ReactNativeWebView.postMessage fails on Web and React Native can't receive height data from the WebView.

This can be circumvented by replacing these calls with window.parent.postMessage as described in https://github.com/react-native-web-community/react-native-web-webview/issues/37, but this creates another problem - now postMessage calls equally trigger events for all WebViews on the page, meaning all WebViews are going to get equal height.

The proper solution would probably be updating react-native-web-webview to implement window.ReactNativeWebView, but I couldn't find an easy way to do it, so I have instead made changes to this package (react-native-autoheight-webview).

The changes are as follows: 0. Unrelated to the main issue, but this package utilizes ViewPropTypes from react-native, however ViewPropTypes was removed from react-native-web (see https://github.com/necolas/react-native-web/issues/1537). In order to fix this and make the package work on Web, change the following in autoHeightWebView/index.js:

-import {StyleSheet, Platform, ViewPropTypes} from 'react-native';
+import {StyleSheet, Platform} from 'react-native';
+import {ViewPropTypes} from 'deprecated-react-native-prop-types';

And install deprecated-react-native-prop-types in your project: npm install deprecated-react-native-prop-types --save Right now deprecated-react-native-prop-types is a dependency of react-native (as of 0.68.2), but it may be removed later, so better install it separately.

  1. Now we need to make calls to window.parent when window.ReactNativeWebView is unavailable. In order to do that, change the following lines in autoHeightWebView/utils.js:

    -    if (
    -      !window.hasOwnProperty('ReactNativeWebView') || 
    -      !window.ReactNativeWebView.hasOwnProperty('postMessage')
    -    ) {
    +    const poster = window.hasOwnProperty('ReactNativeWebView') ? window.ReactNativeWebView :
    +          window.hasOwnProperty('parent') ? window.parent : window;
    +    if (!poster.hasOwnProperty('postMessage')) {

    and

    -    tempZoomedin !== zoomedin && window.ReactNativeWebView.postMessage(JSON.stringify({ zoomedin: tempZoomedin, topic: ${topicString} }));
    +    const poster = window.hasOwnProperty('ReactNativeWebView') ? window.ReactNativeWebView :
    +      window.hasOwnProperty('parent') ? window.parent : window;
    +      tempZoomedin !== zoomedin && poster.postMessage(JSON.stringify({ zoomedin: tempZoomedin, topic: ${topicString}, target: document.title }));
  2. Now we need something for message identification. In order to do that, I'm passing the post's ID to WebView's tag and checking for it in the handleMessage function. For that, you need to make the following changes. 2.1. In autoHeightWebView/utils.js:</p> <pre><code>- window.ReactNativeWebView.postMessage(JSON.stringify({ width: Math.min(width, screen.width), height: height * usingScale, topic: ${topicString} })); + poster.postMessage(JSON.stringify({ width: Math.min(width, screen.width), height: height * usingScale, topic: ${topicString}, target: document.title }));</code></pre> <p>2.2. In autoHeightWebView/index.js:</p> <pre><code>const { + source, style, onMessage, onSizeUpdated, scrollEnabledWithZoomedin, scrollEnabled, } = props;</code></pre> <p>and</p> <pre><code> if (data.topic !== topic) { onMessage && onMessage(event); return; } + if (data.target) { + const title = source.html.match(/<title[^>]*>(.*)<\/title>/i)[1]; + if (data.target != title) return; + }</code></pre> <p>2.3. In your WebView's HTML code, remember to pass the item's ID in the <title> tag, e.g.: <code><title>${props.object.id}</title></code></p> </li> <li> <p>That's all, now you just need to create a package patch with <code>npx patch-package react-native-autoheight-webview</code>.</p> </li> <li> <p>As a bonus point - I've got a weird bug where my WebView's width would gradully reduce. It disappeared after I disabled all possible scrollbars, like this:</p> <pre><code> <AutoHeightWebView scalesPageToFit={true} scrollEnabled={false} nestedScrollEnabled={false} showsHorizontalScrollIndicator={false} showsVerticalScrollIndicator={false} originWhitelist={["*"]} source={{ baseUrl: "", html: html }} /></code></pre> <p>Also in your WebView's HTML code, add this style:</p> <pre><code> <style> body { overflow: hidden; } </style></code></pre> </li> </ol> <p>After this, my WebViews finally got a working autoheight in Android, iOS and, most importantly, Web. If you don't want to follow the steps, you can just grab this patch file: <a rel="noreferrer nofollow" target="_blank" href="https://github.com/iou90/react-native-autoheight-webview/files/8832380/react-native-autoheight-webview%2B1.6.1.txt">react-native-autoheight-webview+1.6.1.txt</a>. Rename it from ".txt" to ".patch" and put it in your project's "patches" directory.</p> <p>I don't think this solution is elegant enough to warrant a Pull Request, but hopefully it will save someone time until <strong>react-native-web-webview</strong> is fixed (if ever).</p> </div> </div> <div class="page-bar-simple"> </div> <div class="footer"> <ul class="body"> <li>© <script> document.write(new Date().getFullYear()) </script> Githubissues.</li> <li>Githubissues is a development platform for aggregating issues.</li> </ul> </div> <script src="https://cdn.jsdelivr.net/npm/jquery@3.5.1/dist/jquery.min.js"></script> <script src="/githubissues/assets/js.js"></script> <script src="/githubissues/assets/markdown.js"></script> <script src="https://cdn.jsdelivr.net/gh/highlightjs/cdn-release@11.4.0/build/highlight.min.js"></script> <script src="https://cdn.jsdelivr.net/gh/highlightjs/cdn-release@11.4.0/build/languages/go.min.js"></script> <script> hljs.highlightAll(); </script> </body> </html>