nolanlawson / emoji-picker-element

A lightweight emoji picker for the modern web
https://nolanlawson.github.io/emoji-picker-element/
Apache License 2.0
1.27k stars 80 forks source link

TypeError: shadowRootNode.getElementById is not a function. #398

Closed Madriix closed 5 months ago

Madriix commented 5 months ago

iOS users often have this error:

Date : 2024/02/16 12:32:32 errorMsg : TypeError: shadowRootNode.getElementById is not a function. (In 'shadowRootNode.getElementById(emo-${emoji.id})', 'shadowRootNode.getElementById' is undefined) errorUrl : https://x.com/v1/others/emoji-picker-element/picker.js errorLine : 1203 errorCol : 66 errorStack : "emojiToDomNode@https://x.com/v1/others/emoji-picker-element/picker.js:1203:66\ncheckZwjSupport@https://x.com/v1/others/emoji-picker-element/picker.js:296:35\ncheckZwjSupportAndUpdate@https://x.com/v1/others/emoji-picker-element/picker.js:1204:20" userAgent : Mozilla/5.0 (iPhone; CPU iPhone OS 15_8_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/15.6.6 Mobile/15E148 Safari/604.1

It only does this with iOS

nolanlawson commented 5 months ago

Hm, that's odd. It's a fairly recent version of iOS as well (15.6).

The line that's throwing is this one:

https://github.com/nolanlawson/emoji-picker-element/blob/82b1f7fabc6a90f7a8b9e05571580e3cfde120a8/src/picker/components/Picker/Picker.js#L435

Unfortunately, without a test to reproduce, I don't know why showRootNode.getElementById is undefined.

Does this only occur on iOS 15? Have you been able to reproduce the error yourself? Is it running as a WebView in a native app, as a homescreen app, in Safari...?

nolanlawson commented 5 months ago

Well I tested if shadowRoot.getElementById is supported in iOS 15.5, and it is:

Screenshot from 2024-02-16 17-40-42

So I'm sorry, but I'm totally stumped. :confused:

Madriix commented 5 months ago

Here are other user agents affected:

errorMsg : TypeError: shadowRootNode.getElementById is not a function. (In 'shadowRootNode.getElementById(emo-${emoji.id})', 'shadowRootNode.getElementById' is undefined) errorUrl : https://x.com/v1/others/emoji-picker-element/picker.js errorLine : 1203 errorCol : 66 errorStack : "emojiToDomNode@https://x.com/v1/others/emoji-picker-element/picker.js:1203:66\ncheckZwjSupport@https://x.com/v1/others/emoji-picker-element/picker.js:296:35\ncheckZwjSupportAndUpdate@https://x.com/v1/others/emoji-picker-element/picker.js:1204:20" userAgent : Mozilla/5.0 (iPhone; CPU iPhone OS 16_7_5 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.6 Mobile/15E148 Safari/604.1

Date : 2024/02/17 01:59:06 errorMsg : TypeError: shadowRootNode.getElementById is not a function. (In 'shadowRootNode.getElementById(emo-${emoji.id})', 'shadowRootNode.getElementById' is undefined) errorUrl : https://x.com/v1/others/emoji-picker-element/picker.js errorLine : 1203 errorCol : 66 errorStack : "emojiToDomNode@https://x.com/v1/others/emoji-picker-element/picker.js:1203:66\ncheckZwjSupport@https://x.com/v1/others/emoji-picker-element/picker.js:296:35\ncheckZwjSupportAndUpdate@https://x.com/v1/others/emoji-picker-element/picker.js:1204:20" userAgent : Mozilla/5.0 (iPhone; CPU iPhone OS 17_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) CriOS/121.0.6167.171 Mobile/15E148 Safari/604.1

The debug is generated with a window.onerror on a production location. Since this morning, in 12 hours it has detected 205 errors linked to the log that I showed you. I haven't tested with an iPhone. Users are not on an application, but on their browser, probably Safari.

nolanlawson commented 5 months ago

Sorry, but this doesn't really help me narrow it down. getRootNode should always return either a Document or ShadowRoot, and so I don't understand how its getElementById can be undefined:

https://github.com/nolanlawson/emoji-picker-element/blob/82b1f7fabc6a90f7a8b9e05571580e3cfde120a8/src/picker/components/Picker/Picker.js#L434-L435

Are users reporting any visual errors in the component itself? Or are you just seeing this error in your logs?

nolanlawson commented 5 months ago

Also: which version of emoji-picker-element are you using? I just released v1.21.1 which probably won't fix your issue, but will at least give us a different error message that may help debug.

Madriix commented 5 months ago

I updated. But I think I found the problem. Here are the explanations:

1) I use this script:

    <script type="module">
      async function hasIDB() {
        if (typeof indexedDB === 'undefined') {
          return false
        }

        try {
          const idbFailed = await new Promise(resolve => {
            const db = indexedDB.open('test-idb')
            db.onerror = () => resolve(true)
            db.onsuccess = () => {
              indexedDB.deleteDatabase('test-idb')
              resolve(false)
            }
          })
          if (idbFailed) {
            return false
          }
        } catch (e) {
          return false
        }
        return true
      }

      async function polyfillIDB() {
        await import('/v1/others/test/fake-indexeddb.js?v=1708213879284')
        // Can't override the indexedDB global, but we can monkey-patch it
        for (const func of ['open', 'deleteDatabase']) {
          indexedDB[func] = FakeIndexedDB[func].bind(FakeIndexedDB)
        }
        for (const func of ['bound', 'lowerBound', 'upperBound', 'only']) {
          IDBKeyRange[func] = FDBKeyRange[func].bind(FDBKeyRange)
        }
      }

      (async () => {
        if (!(await hasIDB())) {
          await polyfillIDB()
        }
        const {Picker} = await import('/v1/others/emoji-picker-element/index.js?v=1708213879284')
        //const picker = new Picker()
        //document.body.appendChild(picker)
      })()

    </script>

2) The menu is then called but not in the mobile version: - I don't use the emoji menu on the mobile version, because They manage to have emojis via their keyboard for certain people.

The code to detect mobile and non-mobile that I use:

function is_mobile_width() {
    const n = navigator;
    if (n && n.userAgentData && n.userAgentData.mobile) {
        return true;
    }
    else if (document.body.clientWidth <= 950) {
        return true;
    }
    else {
        return false;
    }
}

So in the end, the thing I should do is not to call the first script in the mobile version. Currently the first script is global, for all users. Sorry for bothering you for nothing.

nolanlawson commented 5 months ago

Interesting. I still don't understand how this caused shadowRootNode.getElementById to not be a function? But if you think the problem is with your app instead of emoji-picker-element then I'm happy to close this issue. Thanks for reporting!

aerovulpe commented 5 months ago

Hey @nolanlawson!

I'm encountering this issue as well.

D.getElementById is not a function. (In 'D.getElementById(`emo-${X.id}`)', 'D.getElementById' is undefined)

User-Agent: Mobile Safari UI/WKWebView iOS Version: 17.2.1

It seems to happen intermittently. I haven't been able to replicate it locally but I can provide some context from Sentry errors.

Thanks for your awesome library!

nolanlawson commented 5 months ago

Hm that's really bizarre. @aerovulpe I suppose you're using the latest version of the package? But Sentry doesn't give you the un-minified stacktrace? And you're also only seeing this on iOS Safari?

aerovulpe commented 5 months ago

I upgraded to 1.21.1 from 1.20.1 before dropping a comment. I haven't seen a Sentry error related to shadowRootNode.getElementById since the upgrade, so I think https://github.com/nolanlawson/emoji-picker-element/commit/19331c6be6de9da9199a43c6aa13e48fd310a952 may have fixed it!

Unfortunately, I don't have the un-minified stack trace, and it only affected certain iOS Safari users, but it seems the issue has been fixed empirically.

I'll keep monitoring, and I'll let you know if anything pops open, but it's probably safe to reclose this.

Thanks again!

nolanlawson commented 5 months ago

Sounds good! Please reopen if the issue crops up again in 1.21.1.