Problem with virtual Keyboard on mobile

Hi,

i have a problem with the complete playcanvas container is pushed up / is offsetting, when trieing to use a input field. It seems the native api is trying to focus the input field element and i couldnt figure out how to prevent this behaviour.

I basically found a good script to make input fields work in playcanvas but now the last problem is the offsetting…

the script looks like:

(function () {
    // iOS positioning is not fun when the keyboard is involved
    // https://blog.opendigerati.com/the-eccentric-ways-of-ios-safari-with-the-keyboard-b5aa3f34228d

    // Needed as we will have edge cases for particlar versions of iOS
    // returns null if not iOS
    function getIosVersion() {
        if (/iP(hone|od|ad)/.test(navigator.platform)) {
            var v = (navigator.appVersion).match(/OS (\d+)_(\d+)_?(\d+)?/);
            var version = [parseInt(v[1], 10), parseInt(v[2], 10), parseInt(v[3] || 0, 10)];
            return version;
        }

        return null;
    }

    const iosVersion = getIosVersion();

    // Add the CSS needed to get the safe area values
    // https://benfrain.com/how-to-get-the-value-of-phone-notches-environment-variables-env-in-javascript-from-css/
    document.documentElement.style.setProperty('--sat', 'env(safe-area-inset-top)');
    document.documentElement.style.setProperty('--sab', 'env(safe-area-inset-bottom)');
    document.documentElement.style.setProperty('--sal', 'env(safe-area-inset-left)');
    document.documentElement.style.setProperty('--sar', 'env(safe-area-inset-right)');


    const app = pc.Application.getApplication();
    let inputDom = null;

    function createInputDom() {
        if (inputDom) {
            inputDom.remove();
        }

        inputDom = document.createElement('input');
        inputDom.setAttribute('type', 'text');
        inputDom.setAttribute('autofocus', 'false');
        inputDom.style.position = 'absolute';
        inputDom.style.fontFamily = 'Coke, Arial, sans-serif';
        inputDom.style.color = 'white';
        inputDom.style.background = "rgba(0,0,0,0.9)";
        inputDom.style.borderRadius = "30px";
        inputDom.style.boxShadow = 'none';
        //inputDom.style.outline = 'none';
        inputDom.style.paddingLeft = '15px';
        inputDom.style.paddingRight = '15px';
        inputDom.style.margin = '0px';
        inputDom.style.visibility = 'hidden';
        inputDom.style.zIndex = 1000;
        inputDom.style.marginTop = '40vh';
       
        if(window._isMobile)
        {

        }
       
        resetStyle();
        
        inputDom.value = '';
        document.body.appendChild(inputDom);
    }

    createInputDom();

    let domInPlace = false;
    let currentInputFieldScript = false;
    let iosResizeTimeoutHandle = null;
    const iosResizeTimeoutDuration = 2100;


    function onInputFieldClick(inputFieldScript, inputEvent) {
        inputEvent.stopPropagation();
        showDom(inputFieldScript, inputEvent);
    }

    function showDom(inputFieldScript, inputEvent) {
        // If it's the same input field then do nothing
        if (currentInputFieldScript === inputFieldScript) {
            return;
        }

        // If we have clicked on a different input field then switch to that
        if (currentInputFieldScript && currentInputFieldScript !== inputFieldScript) {
            onBlur();
        }

        currentInputFieldScript = inputFieldScript;

        if (inputDom.style.visibility !== 'visible') {
            // Check if it's a touch event
            if (inputEvent.changedTouches) {
                inputEvent.event.preventDefault();
                domInPlace = false;
            } else {
                domInPlace = true;
            }

            inputDom.style.visibility = 'visible';
            inputDom.onblur = onBlur;
            inputDom.addEventListener('keydown', onKeyDown);
            inputDom.addEventListener('keyup', onKeyUp);
        }

        inputDom.value = inputFieldScript.value;
        inputDom.maxLength = inputFieldScript.maxLength;
        inputDom.placeholder = inputFieldScript.placeHolder;

        inputDom.pattern = null;
        inputDom.spellcheck = false;
        switch (inputFieldScript.inputType) {
            case 'text': {
                inputDom.type = 'text';
                inputDom.spellcheck = true;
            } break;
            case 'text no spellcheck': {
                inputDom.type = 'text';
            } break;
            case 'number': {
                inputDom.type = 'number';
                inputDom.pattern = "[0-9]*";
            } break;
            case 'decimal': {
                inputDom.type = 'number';
            } break;
            case 'email': {
                inputDom.type = 'email';
            } break;
            case 'password': {
                inputDom.type = 'password';
            } break;
            default: {
                inputDom.type = 'text';
                inputDom.spellcheck = true;
            } break;
        }

        inputDom.enterKeyHint = inputFieldScript.enterKeyHint;

        inputDom.focus();
        updateStyle();

        currentInputFieldScript.entity.element.on('resize', updateStyle);
    }

    function onElementSwitch() {
        currentInputFieldScript.entity.fire('uiinput:updatevalue', inputDom.value);
        currentInputFieldScript.entity.element.off('resize', updateStyle);
        
        // Workaround: If the input field was changed to be a password, 
        // changing it to anything else doesn't update the keyboard layout
        // correctly
        if (currentInputFieldScript.inputType === 'password') {
            createInputDom();
        }

        currentInputFieldScript = null;
    }

    function onBlur() {
        inputDom.onblur = null;
        inputDom.removeEventListener('keydown', onKeyDown);
        inputDom.removeEventListener('keyup', onKeyUp);
        inputDom.style.visibility = 'hidden';

        onElementSwitch();
    }

    function onKeyDown(event) {
        event.stopPropagation();
    }

    function onKeyUp(event) {
        event.preventDefault();
        event.stopPropagation();
        
        // Enter key
        if (event.keyCode === 13) {
            inputDom.blur();
        }
    }
    
    function resetStyle() {
        const leftSafeArea = parseInt(getComputedStyle(document.documentElement).getPropertyValue("--sal"));
        const rightSafeArea = parseInt(getComputedStyle(document.documentElement).getPropertyValue("--sar"));

        inputDom.style.left = '20px';
        inputDom.style.height = '40px';
        inputDom.style.width = (window.innerWidth - 64 - leftSafeArea - rightSafeArea) + 'px';
        inputDom.style.fontSize = '100%';
        //inputDom.style.top = '20px';
        //inputDom.style.top = '50%';
        //inputDom.style.marginTop = 'env(safe-area-inset-top)';
        //inputDom.style.marginTop = '60%';
        inputDom.style.marginLeft = 'env(safe-area-inset-left)';
        inputDom.style.bottom = null;
        //inputDom.style.bottom = "250px";
    }
    
    function updateStyle() {
        
        if (currentInputFieldScript) {
            if (domInPlace && currentInputFieldScript.entity.element.screenCorners) {
                const corners = currentInputFieldScript.entity.element.screenCorners;
                const devicePixelRatio = Math.min(app.graphicsDevice.maxPixelRatio, window.devicePixelRatio);

                inputDom.style.left = ((corners[0].x / devicePixelRatio) - 2) + 'px';
                inputDom.style.bottom = ((corners[0].y / devicePixelRatio) - 2) + 'px';
                inputDom.style.top = null;

                const width = ((corners[2].x - corners[0].x) / devicePixelRatio) - 20;
                const height = (corners[2].y - corners[0].y) / devicePixelRatio;

                inputDom.style.width = width + 'px';
                inputDom.style.height = height + 'px';

                inputDom.style.fontSize = Math.round(height * 0.5) + 'px';
            } else {
                resetStyle();
            }
        }
        
        

        if (window._isMobile) {
            const viewport = window.visualViewport;
            if (viewport) {
                const keyboardHeight = window.innerHeight - viewport.height - viewport.offsetTop;
                console.log(keyboardHeight);
                //inputDom.style.top = null;
                //inputDom.style.bottom = (keyboardHeight + 20) + 'px';
                //inputDom.style.marginTop = (75) + '%';
                //inputDom.style.bottom = (25) + '%';
            }
            
            /*
            const viewportHeight = window.innerHeight;
            const visualViewportHeight = window.visualViewport ? window.visualViewport.height : viewportHeight;
            const keyboardHeight = viewportHeight - visualViewportHeight;

            // Calculate the offset to move inputDom above the virtual keyboard
            const offset = keyboardHeight > 0 ? keyboardHeight + 20 : 0;
            
            // Adjust the position of inputDom
            // inputDom.style.top = 'auto';
            inputDom.style.bottom = offset + 10 + 'px';
            */
        }

       
    }

    function onResize() {
        if (iosVersion && !iosResizeTimeoutHandle) {
            app.off('uiinput:clicked', onInputFieldClick);
            iosResizeTimeoutHandle = setTimeout(onIosResizeTimeout, iosResizeTimeoutDuration);
        }

        // Resize the input on the next frame
        setTimeout(() => {
            updateStyle();
        });
    }

    function onIosResizeTimeout() {
        app.on('uiinput:clicked', onInputFieldClick);
        iosResizeTimeoutHandle = null;
    }

    // !!! On iOS, there is some code in the boilerplate to ensure
    // that the canvas fills the screen when rotating from landscape
    // to portrait. Unfortunately, this means we can't bring up the keyboard
    // until two seconds after a resize event :(
    if (iosVersion) {
        iosResizeTimeoutHandle = setTimeout(onIosResizeTimeout, iosResizeTimeoutDuration);
    } else {
        app.on('uiinput:clicked', onInputFieldClick);
    }
    app.graphicsDevice.on('resizecanvas', onResize);
})();

/*
backup get texture from assets

 /*
        // Apply the image background
        const textureAsset = app.assets.get(179165145, 'texture');
        if (textureAsset) {
            console.log(textureAsset.getFileUrl());
            inputDom.style.backgroundImage = `url(${textureAsset.getFileUrl()})`;
            inputDom.style.backgroundSize = 'cover';
        }else{
            console.error("Texture asset with ID '179165145' not found.");
        }
        */

        /* load custom asset from url
        var asset = new pc.Asset("My Asset Name", "texture", {
        url: "https://c1.staticflickr.com/9/8873/18598400202_3af67ef38f_q.jpg",
        });

        this.app.assets.add(asset);

        asset.ready(
        function () {
            console.log(this.app.assets.find("My Asset Name"));
        }.bind(this)
        );

        this.app.assets.load(asset);
        */

        // Apply the image background by texture name
        /*
        const asset = app.assets.get(179165145);
        console.log(asset.name);
        if (asset) {
            if (asset.resource) {
                //inputDom.style.backgroundImage = `url(${asset.getFileUrl()})`;
                //inputDom.style.backgroundSize = 'cover';
                inputDom.style.backgroundImage = asset.resource;
                inputDom.style.backgroundSize = 'cover';
                console.log("Texture found and applied:" + asset.resource);
            } else {
                textureAsset.ready(() => {
                    inputDom.style.backgroundImage = asset.resource;
                    inputDom.style.backgroundSize = 'cover';
                    console.log("Texture loaded and applied:" + asset.resource);
                });
                app.assets.load(asset);
            }
        } else {
            console.error(`Texture asset with name '${textureName}' not found.`);
        }

        */

Has somebody an idea how to just align an element on top of the virutal keyboard and prevent the virtual keyboard from pushing the complete application canvas to focus on the input element?

thank you very much!