Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

iframe prevents iScroll scrolling on mobile Safari

I am using iScroll on my mobile enable website (using iPhone here) to scroll inside a div.

In this this div, I have an iframe with a fixed height like this:

<body>
  <div id="iscroller">
    <iframe id="theIframe"></iframe>
    Other stuff
  </div>
</body>

Now, while scrolling within the div, everything works as expected but I cannot scroll when the scrolling gesture begins on the iframe.

The problem is described here pretty well: https://github.com/cubiq/iscroll/issues/41

So, I used the css workaround from that post by applying pointer-events:none to the iframe.

Now scrolling works perfectly but I cannot click any links which are defined within the iframe because all click/touch events on the iframe seems to be blocked due to pointer-events: none.

So, I thought:

"Ok, while the user scrolls, I need pointer-events:none. If he is not scrolling (and instead clicking), I must set pointer-events:auto in order to let the click/touch events pass."

So I did this:

CSS

#theIframe{pointer-events:none}

JavaScript

$("#theIframe").bind("touchstart", function(){
  // Enable click before click is triggered
  $(this).css("pointer-events", "auto");
});

$("#theIframe").bind("touchmove", function(){
  // Disable click/touch events while scrolling
  $(this).css("pointer-events", "none");
});

Even adding this doesn't work:

$("#theIframe").bind("touchend", function(){
  // Re-enable click/touch events after releasing
  $(this).css("pointer-events", "auto");
});

No matter what I do: Either scrolling doesn't work or clicking the link inside the iframe doesn't work.

Doesn't work. Any ideas?

like image 306
Timo Ernst Avatar asked Mar 06 '13 13:03

Timo Ernst


2 Answers

I found the perfect solution. Works great on iOS and Android.

The basic idea is to put a div layer on top of that iframe. This way scrolling works smoothly.

If the user wants to tap/click on an element on that iframe I simply catch that click on the layer, save the x and y coordinates and trigger a click event on the iframe's content at these coordinates:

HTML:

<div id="wrapper">
    <div id="layer"></div>
    <iframe id="theIframe"></iframe>
</div>
Other stuff

CSS:

#layer{
    position:absolute;
    opacity:0;
    width:100%;
    height:100%;
    top:0;
    left:0;
    right:0;
    bottom:0;
    z-index:2
}

JavaScript:

$('#layer').click(function(event){
    var iframe = $('#theIframe').get(0);
    var iframeDoc = (iframe.contentDocument) ? iframe.contentDocument : iframe.contentWindow.document;

    // Find click position (coordinates)
    var x = event.offsetX;
    var y = event.offsetY;

    // Trigger click inside iframe
    var link = iframeDoc.elementFromPoint(x, y);
    var newEvent = iframeDoc.createEvent('HTMLEvents');
    newEvent.initEvent('click', true, true);
    link.dispatchEvent(newEvent);
});
like image 158
Timo Ernst Avatar answered Nov 06 '22 23:11

Timo Ernst


I found a solution for this, it happens to be close to what other guys already mentioned on github but this may be useful for whoever wants to find a fast working resolution for this problem.

I'm assuming a few things, like there's only one iscroll container, here represented as ID. This is not properly tested and needs refactor. It's working in my project, but I changed it here slightly for the example but I guess you'll easily understand what you need to do:

var $iscroll = $('#iscroll');

document.addEventListener('touchstart', function(e) {

if ($iscroll.find('iframe').length > 0){

    $.each($iscroll.find('iframe'), function(k,v){

        var $parent = $(v).parent().first();

        if ($parent.find('.preventTouch').length == 0){

            $('<div class="preventTouch" style="position:absolute; z-index:2; width:100%; height:100%;"></div>')
                .prependTo($parent);

        };

        $parent
            .css('position', 'relative').css('z-index', 1);

    });

    $iscroll.find('.preventTouch').on('click', function(e){
        e.preventDefault();
        e.stopPropagation();
        return false;
    });

};

};

document.addEventListener('touchend', function(e) {

if ($iscroll.find('iframe').length > 0){

    setTimeout(function(){

        var $iscroll = $('#iscroll');

        $iscroll.find('.preventTouch').remove();
        $iscroll.find('iframe').css('z-index', '');
        $iscroll.find('.preventTouch').off('click');

    }, 400);

};

};

Thanks for looking!

like image 22
punkbit Avatar answered Nov 07 '22 00:11

punkbit