swiper中使用iframe导致无法滑动的3个解决方案

看到这个标题,很多同学都会疑惑,为什么swiper中要放iframe呢?事实上,当我遇到这个需求前,我也没想到会有这样的骚操作,swiper中嵌入网站,每次滑动切换一个网站?想想听炫酷的,可做起来就不酷了。

当你开开心心的把iframe放到每一个swiper-slide中后,你会发现页面不能滑动了,你哭了,你绝望了。你打开谷歌,搜解决方案时,你会发现这类相关库的作者遇到这种bug比你还绝望:

这个swipe作者的回答翻译过来的大致意思是:“大哥了这问题不好搞我也不想搞了,拜拜”
而swiper的作者回答的更直接:

"这是iframe的锅,我没法在iframe里侦听到事件"
然后close了这个issue...
难道就没有办法解决这个问题吗?终于,经过各种百度谷歌翻issue,我找到了三种解决这个问题的方法,正好应对了三种不同的情况,且听我娓娓道来。

最简单的办法:pointer-events

pointer-events是css3新增的属性,它可以让设置这个属性的元素无法侦听任何事件,具体的API可以参考这里pointer-events
我们可以给iframe设置这个属性之后,网站之间就可以来回切换。但是,这种方法有个弊端,正如官方介绍所说,它会是整个元素无法侦听事件,这会网站内的所有交互都无法操作了,除非你得页面是个纯展示性质的静态网站。
所以,如果你想用这种方法解决无法滑动的问题,首先要确保你的网站是纯静态没有任何事件需要触发的。

侦听iframe内的滑动事件

这种方法适合iframe内的网址和外部的网址在同一域下,因为我们需要直接在父级页面监听iframe内的事件:

//  用来计算滑动方向的变量
let startX,startY,endX,endY,distanceX,distanceY; 
let iframe = document.querySelector('iframe');
const MOVE_RATE = 2;
iframe.contentWindow.addEventListener('touchstart',function (e) {
    startX = e.targetTouches[0].pageX;
    startY = e.targetTouches[0].pageY;
});
iframe.contentWindow.addEventListener('touchmove',function (e) {
    endX = e.targetTouches[0].pageX;
    endY = e.targetTouches[0].pageY;
    distanceX = endX - startX;
    distanceY = endY - startY;
    if(Math.abs(distanceX) > Math.abs(distanceY) && distanceX > (body.clientWidth/MOVE_RATE)){
        //slideNext 和 slidePrev)()都是swiper的API
        myswiper.slideNext()
    }else if(Math.abs(distanceX) > Math.abs(distanceY) && distanceX < (body.clientWidth/MOVE_RATE)){
        myswiper.slidePrev()
    }else{
        console.log('点击未滑动');
    }
});
复制代码

这个方案的思路是先获取iframe内部的window,然后监听windowtouchstart和touchmove事件,根据滑动方向和距离判断是左滑或者右滑,调用swiper向前或者向后滑的API即可。

这个方案其实百度一搜就有,如果你有研究过swiper中嵌入iframe话一定见过这个方案,但其实,这个方案有两个弊端:

一是当swiper页面3个以上时,由于touchmove事件是多次触发的,导致多次调用slideNext或者slidePrev方法,用户轻轻滑一下页面,直接会滑到最后一页;

第二个问题是是不能处理跨域的网址,事实上有些需求是需要嵌入一些合作方的网址的,而这些网址不一定是同域的;

第一种情况我的解决办法是,声明两个变量,分别作为向左滑和向右滑的计数器,让touchmove事件仅执行一次,同时,当完成一次滑动后清空计数器,保证下次同方向的滑动:

let left_slide_count = 0, right_slide_count = 0;
const COUNT_LIMIT = 2;
...
...
if(Math.abs(distanceX) > Math.abs(distanceY) && distanceX > (body.clientWidth/MOVE_RATE)){
   //slideNext 和 slidePrev)()都是swiper的API
   left_slide_count ++;
   if( left_slide_count < COUNT_LIMIT ){
       myswiper.slideNext()
   }
}else if(Math.abs(distanceX) > Math.abs(distanceY) && distanceX < (body.clientWidth/MOVE_RATE)){
   right_slide_count ++;
   if( right_slide_count < COUNT_LIMIT ){
       myswiper.slidePrev()
   }
}else{
   console.log('点击未滑动');
}
复制代码

同时,我们还需要在swiper中加一些配置:

let swiper = new Swiepr({
    ...
    ...
    on: {
       //当滑动结束时,清空计数器
       slideChangeTransitionEnd: function(){
         left_slide_count = 0;
         right_slide_count = 0;
       },
    }
})
复制代码

至于第二个问题,其实可以用postMessage来解决。

使用postMessage解决嵌入跨域网站的滑动

这种方法其实和上种方法的解决思路大致相同,不过是把滑动的监听放到内部网站中,然后把滑动信息通过postMessage传给父级页面,然后父级页面调用swiepr的API:

let left_slide_count = 0, right_slide_count = 0;
const COUNT_LIMIT = 2;
...
...
if(Math.abs(distanceX) > Math.abs(distanceY) && distanceX > (body.clientWidth/MOVE_RATE)){
   //slideNext 和 slidePrev)()都是swiper的API
   left_slide_count ++;
   if( left_slide_count < COUNT_LIMIT ){
       window.parent.postMessage("right-move", "*")
   }
}else if(Math.abs(distanceX) > Math.abs(distanceY) && distanceX < (body.clientWidth/MOVE_RATE)){
   right_slide_count ++;
   if( right_slide_count < COUNT_LIMIT ){
       window.parent.postMessage("left-move", "*")
   }
}else{
   console.log('点击未滑动');
}

复制代码

父级页面需要监听postMessage事件:

window.postMessage("message", function(e){
    if(e.data == "right-move"){
       if( left_slide_count < COUNT_LIMIT ){
         myswiper.slideNext()
       }
    }else if(e.data == "left-move"){
       if( right_slide_count < COUNT_LIMIT ){
         myswiper.slidePrev()
       }
    }
})
复制代码

当然,这种方法也要做计数器的处理。此外,这种方法也有两个弊端,第一个是当你滑动一下屏幕时页面可以正常切换,但是手一直放在屏幕上滑不离开的话,屏幕达到某个临界值会在两个页面不停切换,直到你松手;第二个弊端是它需要在嵌入的网站中植入一段js脚本才行,如果你想嵌入纯第三方的网站比如淘宝等,那这种方法是行不通的。

那么有没有一种方法可以直接嵌入第三方网站并支持滑动呢,很抱歉确实没有找到,不多我在翻issue时找到了一个叫iframeTracker的库,它可以监听到iframe中的点击事件(当然,这是模拟出来的,事实上并不能监听到),虽然它是一个jquery库,但是内部源码很简单,可以轻松转成js的,做PC端的朋友同时需要监听到iframe中的点击事件的可以看看这个库,我在这里也简单介绍一下这个库的实现:

在PC端上,我们是可以监听到iframe的mouseovermouseout事件的,然后,在页面外写一个input表单,当我们鼠标悬浮在iframe上时,让input表单执行focus事件,当我们在iframe上点击时,会触发input的blur事件,同时也会触发windowblur 事件,这时我们监听windowblur事件,就算捕获到iframe的click事件了。

至于监听移动端的touch事件,各位朋友们有没有好主意呢?

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值