h5跳app的踩坑之路

2,588 阅读5分钟

在h5中是无法直接判断是否安装app的,我们只能使用间接的方式。先尝试打开app,因为正常情况下打开了app,我们的h5会变成后台,app变成前台。所以我们判断一定时间内,如果h5变成了后台运行了,就说明app打开了,就不去跳转下载了。

那么,既然思路在这里,我们就需要解决以下几个问题

  • 判断机型Android或ios
  • 判断打开我们h5的浏览器类型
  • 打开app方式
  • 延时代码,用于app未打开,去下载app
  • 不同机型不同浏览器下载app

ok、撸起袖子开干

判断机型navigator.userAgent

一般我们写h5都需要判断是Android还是ios、因为身边机型有限,网上找了很多各家的判读方法,大抵如下:

var browser={
  versions:function(){
    var u = navigator.userAgent;
    var app = navigator.appVersion;
    return {
      trident: u.indexOf('Trident') > -1, //IE内核
      presto: u.indexOf('Presto') > -1, //opera内核
      webKit: u.indexOf('AppleWebKit') > -1, //苹果、谷歌内核
      gecko: u.indexOf('Gecko') > -1 && u.indexOf('KHTML') == -1,//火狐内核
      mobile: !!u.match(/AppleWebKit.*Mobile.*/), //是否为移动终端
      ios: !!u.match(/\(i[^;]+;( U;)? CPU.+Mac OS X/), //ios终端
      android: u.indexOf('Android') > -1 || u.indexOf('Adr') > -1 || u.indexOf('Linux') > -1, //android终端
      iPhone: u.indexOf('iPhone') > -1 , //是否为iPhone或者QQHD浏览器
      iPad: u.indexOf('iPad') > -1, //是否iPad
      webApp: u.indexOf('Safari') == -1, //是否web应该程序,没有头部与底部
      weixin: u.indexOf('MicroMessenger') > -1, //是否微信 (2015-01-22新增)
      qq: u.match(/\sQQ/i) == " qq", //是否QQ
      browser: navigator.userAgent.indexOf('MQQBrowser') >-1
    };
  }(),
  language:(navigator.browserLanguage || navigator.language).toLowerCase()
}

注意:以上判断QQ的方式涵盖了QQ浏览器。也就是browser.versions.qq不能区分是QQ浏览器还是QQ内置浏览器打开。

这里,我们使用另外一种方式来兼容处理一下、排除QQ浏览器

browser.versions.qq && !browser.versions.browser
补充发现:oppo R17 Android 10 qq内置浏览器,既存在QQ也存在MQQBrowser 所以用以上方式实现不了,但是它里面有个QQTheme
我们就再处理一下这种情况
browser.versions.qq && navigator.userAgent.match(/QQTheme/i) == 'QQTheme'

哎!太烦了....

判断打开我们h5的浏览器类型

使用以上的设备判断方式,我们需要区分微信和QQ打开我们的h5,因为安卓里面微信和QQ是没有办法直接跳转到我们的app的。而ios的微信里面可以通过设置URL Scheme来实现直接跳转(ios9以上才有效)。我自己测试是ios的qq里面跳转不了,跳转会404,网上有小伙伴说跳转方式使用window.top.location.href可以,but 我测试是不行的。所以最终我们只实现在微信里面直接跳转app。

ok,我们来判断浏览器,实现不同的逻辑,无法跳转的我们让其在浏览器打开。

button.onclick = function() {
   // 获取到当前连接  注意这里只有点击事件才能跳转,直接js模拟点击去跳转是实现不了的。
   QQ或者安卓微信
  if ((browser.versions.qq && !browser.versions.browser) || (browser.versions.android && browser.versions.weixin)){
    // 提示浏览器打开
  } else if (browser.versions.iPad) {
    openApp(
      `iPad链接`,
      goConfirmAddr
    )
  } else if (browser.versions.android || window.screen.width > 500) {  
  //Android的pad没有找到判断的方式,感觉它就是一个大的手机
    openApp(
      `Android的Pad链接`,
      goConfirmAddr
    )
  } else if (browser.versions.android) {
    openApp(
      `安卓打开app的链接`,
      goConfirmAddr
    )
  } else if (browser.versions.ios) {
    openApp(
      `https://ios打开app的链接`,
      goConfirmAddr
    )
  }
}

打开app方式

  • iframe方式
  • a标签方式
  • location方式

iframe方式

var ifr = document.createElement('iframe')
ifr.setAttribute('src', url)
ifr.setAttribute('style', 'display:none')
document.body.appendChild(ifr)

a标签方式

var a = document.createElement('a')
a.setAttribute('href', url)
a.setAttribute('style', 'display:none')
document.body.appendChild(a)
a.click() 

注意:a标签这种方式有可能触发不了,需要手动触发,所以我们推荐location的方式

location方式

window.location.href = url
location.href

在上面我们说过只有ios的微信可以直接唤起app,其实qq应该也可以的。但是需要使用top.location的方式去跳转。但是我测试还是不行,不知道是不是我的写法有问题,如果有发现可以实现的小伙伴一定要留言告诉我哟~

检测唤起app是否成功

其实实际上是检测不了的,但是我们可以曲线救国。设置延时或者查看我们的h5是否被后台运行了

var _clickTime = +new Date()
function check(elsTime) {
  if (elsTime > 3000 || document.hidden || document.webkitHidden) {
  } else {
    callback && callback()
  }
}
//启动间隔20ms运行的定时器,并检测累计消耗时间是否超过3000ms,超过则结束
var _count = 0,
  intHandle
intHandle = setInterval(function() {
  _count++
  var elsTime = +new Date() - _clickTime
  if (_count >= 100 || elsTime > 3000) {
    clearInterval(intHandle)
    check(elsTime)
  }
}, 20)

以上方式也不完美解决它,比如安卓上面需要确认之后再唤起app,这时已经过去3s了,就会触发下载。哎! 求更好的解决办法。

不同机型不同浏览器下载app

这就没啥好讲的了,跟上面差不多。ok我们贴个完整代码吧

   // 判断手机上是否安装了app,如果安装直接打开url,如果没安装,执行callback
    function openApp(url, callback) {
      window.location.href = url
      var _clickTime = +new Date()
      function check(elsTime) {
        if (elsTime > 3000 || document.hidden || document.webkitHidden) {
        } else {
          callback && callback()
        }
      }
      //启动间隔20ms运行的定时器,并检测累计消耗时间是否超过3000ms,超过则结束
      var _count = 0,
        intHandle
      intHandle = setInterval(function() {
        _count++
        var elsTime = +new Date() - _clickTime
        if (_count >= 100 || elsTime > 3000) {
          clearInterval(intHandle)
          check(elsTime)
        }
      }, 20)
    }

    // 去下载
    function goConfirmAddr() {
      // 是否是安卓QQ打开
      if (isIosPad) {
        window.location = 'iPad下载地址'
      } else if (isAndroidPad) {
        window.location = 'Pad下载地址'
      } else if (isIos) {
        window.location = 'ios下载地址'
      } else {
        window.location = 'Android下载地址'
      }
    }

    window.onload = function() {
      let button = document.getElementById('btn')
      button.onclick = function() {
        if (isAndroidQQ || isAndroidWechat || isIosQQ) {
          // 提示浏览器打开
        } else if (isIosPad) {
          openApp(`XXXXXX`, goConfirmAddr)
        } else if (isAndroidPad) {
          openApp(`XXXXXX`, goConfirmAddr)
        } else if (isAndroid) {
          openApp(`XXXXXX`, goConfirmAddr)
        } else if (isIos) {
          openApp(`XXXXXX`, goConfirmAddr)
        }
      }
    }

补充一下:如果没有app,ios打开那个链接会404。解决方法是把我们的ios打开链接的地址代理到我们的h5下载页,直接去下载。