无法在iOS <12

问题描述

我在触发旧版iOS设备上的文件对话时遇到问题,特别是似乎仍在运行iOS 12的设备上。

我正在使用React-Dropzone包为文件创建一个放置区,而且还添加了一种方式来标记区域以打开文件对话框以选择文件

然后我使用Hammerjs检测onTab事件。

通过添加一个调用onTab时要触发的警报,我可以确定的是onTab事件正在触发,而正是要打开该对话框的功能是不会触发在旧版iOS设备上打开文件对话框。

const FileUploadDropzone = () => {
...
        const {getRootProps,getInputProps,open,inputRef} = useDropzone({
            // disable click and keydown behavior
            noClick: true,noKeyboard: true,});

        const handleTap = useCallback(() => {
            // specific function created by React-Dropzone to open the dialog
            open();
            // also tried to trigger the input click directly using a ref (have confirmted that the input is correctly referenced)
            inputRef.current.click();
        },[allowInteract,uploading,open]);

        return (
            <Hammer onTap={handleTap}>
                <div {...getRootProps()}>
                    <input {...getInputProps()}/>
                    {children}
                </div>
            </Hammer>
        );
    };

根据我所阅读的内容,输入的样式不能设置为display:none,如果设置为input { visibility: hidden; height: 0px; width: 0px; } ,则无法以编程方式触发打开文件对话框。

所以我将样式设置如下:

<h1> CovertCast </h1>
<form action="" method="post">
    {% csrf_token %}
  <label>url:
    <input type="url" name="deine-url" value="https://">
  </label>
    <button type="submit">Get Screenshot</button>
</form>

{% load static %}
<img src="/media/screenshot_image.png" class="bild"/>

<form action="/modulated.html" method="post">
    {% csrf_token %}

    <button type="submit">Modulate</button>
</form>
<img src="/media/modulated_image.png" alt="abc"/>

我还试图做的是将输入和传递给组件的子元素包装在标签中,希望可以更好地支持单击标签以打开对话框,但这还是行不通的。

所以现在我很茫然,我不知道如何使它在这些旧版iOS

解决方法

该问题似乎与以下问题有关:在自定义点击事件与通过编程方式调用输入点击事件之间存在任何延迟时,iOS 12上的Safari不喜欢它。由于在我的案例中,输入内容深深地嵌套在DOM中,因此当事件冒泡时间过长时,就会引起问题。

要解决此问题,我必须创建一个hack,将文件输入附加到DOM中,以使其尽可能像<body>一样孙子。

我紧跟本文,利用React.createPortal将输入追加到组件外部。 https://www.jayfreestone.com/writing/react-portals-with-hooks

我在React应用程序顶部<div>的底部创建了一个用于创建空div的函数。

function usePortal(cssSelector) {
    const rootElemRef = React.useRef(document.createElement('div'));

    useEffect(
        function setupElement() {
            const {current} = rootElemRef;
            const parentElem = document.querySelector(cssSelector);
            parentElem?.appendChild(current);
            return function removeElement() {
                current.remove();
            };
        },[id]
    );

    return rootElemRef.current;
}

然后创建了一个组件,该组件将为其子项附加自定义div,该自定义项由上述函数创建

const Modal = ({children}) => {
    const target = usePortal('#react-root-div');
    return ReactDOM.createPortal(children,target);
};

然后将输入元素与Modal组件包装在一起,它将(几乎)附加到DOM树的底部。

    <Modal>
        <input {...getInputProps()} />
    </Modal>