yairEO / tagify

🔖 lightweight, efficient Tags input component in Vanilla JS / React / Angular / Vue
https://yaireo.github.io/tagify/
Other
3.56k stars 436 forks source link

Intermittent error in setRangeAtStartEnd #1338

Closed RichardD2 closed 6 months ago

RichardD2 commented 6 months ago

Prerequisites

Explanation

This is hard to reproduce in a demo, since the issue is intermittent.

I am using the latest version of Tagify in an <iframe> hosted in a Bootstrap 4 modal. The setup is fairly simple:

<input type="email" multiple pattern="^([^@]+@[^@]+\.[^@]+)([,;][^@]+@[^@]+\.[^@]+)*$" class="tagify tagify-email">
document.addEventListener("DOMContentLoaded", () => {
    "use strict";
    console.debug("Selection", document.getSelection());

    document.querySelectorAll(".tagify-email").forEach(el => {
        const whitelist = [];
        const dropdown = {};

        const listId = el.getAttribute("list");
        if (listId) {
            const list = document.getElementById(listId);
            if (list) {
                list.querySelectorAll("option").forEach(opt => {
                    whitelist.push({ value: opt.value });
                    dropdown.enabled = 0;
                });
            }
        }

        const tagify = new Tagify(el, {
            delimiters: ",|;",
            backspace: "edit",
            pattern: /^[^@@]+@@[^@@]+\.[^@@]+$/,
            originalInputValueFormat: (valuesArr) => valuesArr.map((item) => item.value).join(";"),
            dropdown: dropdown,
            whitelist: whitelist,
            tagTextProp: "text"
        });

        $(el).data("Tagify", tagify);
    });
});

This intermittently results in an error in Firefox:

Uncaught TypeError: n is null
    setRangeAtStartEnd
    addTags
    loadOriginalValues
    j

When that happens, the console.debug line I added shows that document.getSelection() returns null. The first Tagify input shows the tags, but is non-functional - no tags can be added or removed. Any subsequent Tagify inputs are not modified, since the initialization script has crashed.

I suspect this may be a timing issue - perhaps the page load is triggered before the Bootstrap modal is fully shown? I can't see any indication in the MDN documentation that this method would ever return null, but that's what's happening to me.

I think the fix would be to add a check for null before testing whether sel.focusNode is an element:

var sel = document.getSelection()
if( !sel ) return;

// do not force caret placement if the current selection (focus) is on another element (not this tagify instance)
if( sel.focusNode instanceof Element && !this.DOM.input.contains(sel.focusNode) ) {

My current workaround is to delay initialization for 1s if document.getSelection() returns null when the DOMContentLoaded event fires.

RichardD2 commented 6 months ago

I've managed to create a demo page on JSFiddle which consistently reproduces the problem in Firefox:

Demo

If you keep the console open and click the button, you will see that document.getSelection() is always returning null in the hidden <iframe>, and Tagify generates an error:

12:15:53.173 Uncaught TypeError: i is null
    setRangeAtStartEnd https://cdn.jsdelivr.net/npm/@yaireo/tagify:34
    addTags https://cdn.jsdelivr.net/npm/@yaireo/tagify:34
    loadOriginalValues https://cdn.jsdelivr.net/npm/@yaireo/tagify:34
    Z https://cdn.jsdelivr.net/npm/@yaireo/tagify:34
    <anonymous> about:srcdoc:34
    EventListener.handleEvent* about:srcdoc:30
tagify:34:54799

If you click the button again, the iframe will be displayed, and you will see that the input is non-functional.

This only seems to happen if the input has an initial value, and the iframe is not visible when the content page loads. If you remove the initial value, no error is generated.

Interestingly, the demo doesn't error in Chrome.

RichardD2 commented 6 months ago

Looks like this is a Firefox "feature": 827585 - window.getSelection() returns null for hidden iframe

That was reported in 2013, and hasn't been updated since October 2022.

yairEO commented 6 months ago

Thanks for the detailed report. I've just voted on the bug, and so should you, in hope this will speed it having a fix: (I saw you commented there, which is very good)

image

Since this is a browser bug (as Tagify is a very very complex component I've seen many browser bugs involved in different scenarios and it is quite frustrating), I cannot do much here. It's up to the implementation developer (you) to come up with a workaround.

Great job investigating this and finding the Firefox bug page!