Scrolling issues with ExtJS 5 app inside IFrame

2020-08-17 05:19发布

问题:

Hy,

this is what my test page looks like:

The blue area is the parent page and the green area is an IFrame which runs an ExtJS application (simple viewport with a label inside).

If the site is executed on a touch device (IPad, Android Tablet etc) it's not possible to scroll the page by "wiping" on the IFrame (the green area). One has to wipe on the blue area to scroll the page.

This had been working correctly in ExtJS v4.2.1 (see links below).

Test-Sites:

https://skaface.leo.uberspace.de/ScrollTest/Ext510/ (not working as expected, using ExtJS v5.1.1)
https://skaface.leo.uberspace.de/ScrollTest/Ext421/ (working as expected, same code but using ExtJS v4.2.1)

The test code:

Parent site (index.html):

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" style="height: 100%;">
<head>
    <title>Test</title>
    <meta http-equiv="content-type" content="text/html; charset=utf-8"/>
    <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
</head>
<body style="margin: 50px; background-color: blue;">
    <iframe src="frame.html" width="100%" height="1400" style="border: none;"></iframe>
</body>
</html>

IFrame (frame.html):

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" style="height: 100%;">
<head>
    <title>Test</title>
    <meta http-equiv="content-type" content="text/html; charset=utf-8"/>
    <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">

    <link rel="stylesheet" type="text/css" href="https://extjs.cachefly.net/ext/gpl/5.1.0/packages/ext-theme-neptune/build/resources/ext-theme-neptune-all-debug.css" />
    <script type="text/javascript" src="https://extjs.cachefly.net/ext/gpl/5.1.0/build/ext-all-debug.js"></script>

    <script type="text/javascript">
        Ext.onReady(function() {
            Ext.create('Ext.container.Viewport', {
                style: { 'background-color': 'yellowgreen' },
                layout: 'fit',
                items: [{
                    xtype: 'label',
                    text: 'Ext version: ' + Ext.versions.extjs.version,
                    margin: 16
                }]
            });
        });
    </script>
</head>
<body>
</body>
</html>

I'd really appreciate a workaround for this since it practically makes my sites useless on mobile devices even though they had been working perfectly fine with ExtJS 4.2.1.

Thanks & best regards

Ps.: I've already posted a bug report in the sencha forums, but since I didn't get any help there until know, I'm also trying my luck on stackoverflow...

回答1:

The behavious is weird , I have seen it before using niceScroll plugin , and many other plugins had also same issue with iframe , anyway check this workaround

I have used Hammer.js jQuery plugin to detect touch gestures on your iframe , if you find any issues concerning sensetivity ( as I dont know what constraints you are looking for ) , you can adjust hammer.js options found on their repo ( like pan threshold , pointers ..etc )

and the code is very simple :

<body id="mainbody"  style="margin: 50px; background-color: blue;">
    <iframe id="myframe"  src="frame.html" width="100%" height="1400" style="border: none;"></iframe>
</body>
<script>
var myBody
$('iframe').load(function(){
  myBody=$(this).contents().find("body");
  myBody.css({"height":"100%","overflow":"hidden"}).hammer({threshold:1}).bind("pan", myPanHandler);
});
function myPanHandler(ev)
{
    $("#mainbody").scrollTop($("#mainbody").scrollTop()-ev.gesture.deltaY)
console.log(($("#mainbody").scrollTop()-ev.gesture.deltaY*0.5)+".."+$("#mainbody").scrollTop())
}
</script>


回答2:

After a lot of digging around inside the framework, I finally found a solution which at least works for me and consists of 2 steps:

1) ExtJS sets the CSS property touch-action of the viewport (the base html element of the IFrame) and its body to the value none. I've simply overwritten it with the value auto:

.x-viewport, .x-viewport > .x-body {
    touch-action: auto;
}

2) The class Ext.container.Viewport calls Ext.plugin.Viewport.decorate(this); in it's creation callback, which adds a listener to the touchmove event of the viewport itself.

Everything that listener does is calling preventDefault(); of the event, which is the reason why scrolling doesn't work anymore on the parent page or the IFrame itself. My fix simply removes the preventDefault() and instead returns false from the touchmove event handler to let the event bubble up the browser chain:

Ext.define('Cbn.overrides.container.Viewport', {
    override: 'Ext.container.Viewport'
}, function() {
    Ext.override(this, {
        onRender: function() {
            this.mon(Ext.getDoc(), {
                touchmove: function(e) {
                    // e.preventDefault(); // Original ExtJS code
                    return false;
                },
                translate: false,
                delegated: false
            });
            this.callParent(arguments);
        }
    });
});

I'm not quite sure if those 2 fixes have any negative implications but so far they seem to do the job for me.

One thing I did realize is that using components with the config scrollable: true inside the IFrame-App still makes problems but since I can avoid that pretty much everywhere it's no issue for me so far...

Working test-site: https://skaface.leo.uberspace.de/ScrollTest/Ext510_fixed/


Edit:
Adjusted solution a little to not constantly throw unhandled JavaScript errors during touch-scrolling (see Error: Failed to execute 'dispatchEvent' on 'EventTarget')