转:https://blog.csdn.net/CameloHuang/article/details/64476385

从html5打开本地的app–如果本地没有app就跳转到下载页面,大家都会认为这是一项很简单的操作。网上的教程也很多,但是可行性都不高。因为手机系统和浏览器型号各不相同,所以兼容性会是让各个前端工程师头疼的问题。我们不妨看一下京东是如何解决的。京东的原代码已经混淆过了,我只能一点点反混淆并注释。

网上的文章千篇一律 都是采用window.location.href的方式打开的,但是这种方法的兼容性非常的渣。在ios的safari浏览器中无法使用,会出现还未打开app就自动跳转到下载页面的情况,影响用户的使用。那么我们来看一下京东是如何解决兼容性的问题。 
下面附上我的代码翻译和注释。 
京东打开app的js代码链接

  1. (function(){
  2. // 判断浏览器
  3. var Navigator = navigator.userAgent;
  4. var ifChrome = Navigator.match(/Chrome/i) != null && Navigator.match(/Version\/\d+\.\d+(\.\d+)?\sChrome\//i) == null ? true : false;
  5. var ifAndroid = (Navigator.match(/(Android);?[\s\/]+([\d.]+)?/)) ? true : false;
  6. var ifiPad = (Navigator.match(/(iPad).*OS\s([\d_]+)/)) ? true : false;
  7. var ifiPhone = (!ifiPad && Navigator.match(/(iPhone\sOS)\s([\d_]+)/)) ? true : false;
  8. var ifSafari = (ifiPhone || ifiPad) && Navigator.match(/Safari/);
  9. var version = ;
  10. ifSafari && (version = Navigator.match(/Version\/([\d\.]+)/));
  11.  
  12. version = parseFloat(version[], );
  13. // 是否从微信打开
  14. var ifWeixin = navigator.userAgent.indexOf("MicroMessenger") >= ; // weixin
  15. var j = false;
  16. var iframe = "plugIn_downloadAppPlugIn_loadIframe";
  17. var t = false;
  18. var i = ;
  19. var B = {};
  20. var b = {};
  21. var selector = null;
  22. var Hquery = {};
  23. // 判断当前使用的js框架是zepto还是jquery
  24. var Query = window.Zepto || window.jQuery ? true : false;
  25. var g = [];
  26. // 是否存在html5的localStorage 存储
  27. var v = window.localStorage ? true : false;
  28. var o = "mdownloadAppPlugInskip";
  29. var p = null;
  30.  
  31. function m() { // 打印时间 例如:2016-5-18
  32. var M = new Date();
  33. var N = M.getFullYear();
  34. var O = M.getMonth() + ;
  35. var L = M.getDate();
  36. strDate = N + "-" + O + "-" + L;
  37. return strDate
  38. }
  39. // 微信相关操作
  40. function r() { // weixin api
  41. WeixinJSBridge.invoke("getInstallState", {
  42. packageName: "com.jingdong.app.mall",
  43. packageUrl: "openApp.jdMobile://"
  44. }, function(M) {
  45. var N = M.err_msg,
  46. L = ;
  47. if (N.indexOf("get_install_state:yes") > -) {
  48. j = true
  49. }
  50. })
  51. }
  52. // 根据是否存在js框架进行dom和时间的绑定
  53. function bind(dom, event, fun) { // bind event
  54. if (Query) {
  55. selector("#" + dom).bind(event, fun)
  56. } else {
  57. selector("#" + dom).addEventListener(event, fun, !)
  58. }
  59. }
  60.  
  61. function z(L) {
  62. var M = (L || "mGen") + (++i);
  63. return M
  64. }
  65. // 微信操作
  66. if (ifWeixin) { // if navigitor is weixin
  67. if (window.WeixinJSBridge && WeixinJSBridge.invoke) {
  68. r()
  69. } else {
  70. document.addEventListener("WeixinJSBridgeReady", r, !)
  71. }
  72. }
  73.  
  74. // 如果存在js框架
  75. if (Query) {
  76. selector = window.$;
  77. Hquery = window.$
  78. } else {
  79. selector = function(obj) {
  80. if (typeof obj == "object") {
  81. return obj
  82. }
  83. return document.querySelector(obj);
  84. };
  85. if (!window.$) {
  86. window.$ = Hquery = selector
  87. } else {
  88. Hquery = window.$
  89. }
  90. }
  91. window.onblur = function() {
  92. for (var L = ; L < g.length; L++) {
  93. clearTimeout(g[L])
  94. }
  95. };
  96. // 设置cookie。
  97. function e(N) {
  98. var M = document.cookie.indexOf(N + "=");
  99. if (M == -) {
  100. return ""
  101. }
  102. M = M + N.length + ;
  103. var L = document.cookie.indexOf(";", M);
  104. if (L == -) {
  105. L = document.cookie.length
  106. }
  107. return document.cookie.substring(M, L)
  108. }
  109. // 设置cookie
  110. function l(N, P, L, Q, O) {
  111. var R = N + "=" + escape(P);
  112. if (L != "") {
  113. var M = new Date();
  114. M.setTime(M.getTime() + L * * * );
  115. R += ";expires=" + M.toGMTString()
  116. }
  117. if (Q != "") {
  118. R += ";path=" + Q
  119. }
  120. if (O != "") {
  121. R += ";domain=" + O
  122. }
  123. document.cookie = R
  124. }
  125.  
  126. // 打开的链接集合
  127. function F(L) {
  128. var url = {
  129. downAppURl: "http://h5.m.jd.com/active/download/download.html?channel=jd-m",
  130. downAppIos: "http://union.m.jd.com/download/go.action?to=http%3A%2F%2Fitunes.apple.com%2Fcn%2Fapp%2Fid414245413&client=apple&unionId=12532&subunionId=m-top&key=e4dd45c0f480d8a08c4621b4fff5de74",
  131. downWeixin: "http://a.app.qq.com/o/simple.jsp?pkgname=com.jingdong.app.mall&g_f=991850",
  132. downIpad: "https://itunes.apple.com/cn/app/jing-dong-hd/id434374726?mt=8",
  133. inteneUrl: "openApp.jdMobile://360buy?type=1",
  134. inteneUrlParams: null,
  135. openAppBtnId: "",
  136. closePanelBtnId: "",
  137. closePanelId: "",
  138. closeCallblack: null,
  139. closeCallblackSource: null,
  140. cookieFlag: null,
  141. noRecord: false,
  142. sourceType: "JSHOP_SOURCE_TYPE",
  143. sourceValue: "JSHOP_SOURCE_VALUE",
  144. openAppEventId: "MDownLoadFloat_OpenNow",
  145. closePanelEventId: "MDownLoadFloat_Close"
  146. };
  147. if (L) {
  148. for (var M in L) {
  149. if (M && L[M]) {
  150. url[M] = L[M]
  151. }
  152. }
  153. }
  154. return url
  155. }
  156. // 敲黑板 重点内容。看京东是怎么解决兼容问题的。
  157. function openApp(N, L) { // openApp
  158. var R = h(N); //获取相对应的url
  159. var O = null;
  160. if (ifWeixin) { // 如果是微信端
  161. var M = null;
  162. if (j) {
  163. M = R
  164. } else {
  165. M = N.downWeixin
  166. }
  167. location.href = M; // 直接使用location.href打开
  168. return
  169. }
  170. if (ifiPad) { // 如果是ipad
  171. O = N.downIpad
  172. } else {
  173. if (ifiPhone) { // 如果是iphone
  174. O = N.downAppIos
  175. } else {
  176. O = N.downAppURl
  177. }
  178. }
  179.  
  180. if (ifChrome) { // 如果是chrome
  181. if (ifAndroid) { //安卓浏览器
  182. var Q = R;
  183. R = y(Q);
  184. // 延后50毫秒
  185. setTimeout(function() {
  186. window.location.href = R
  187. }, )
  188. }
  189. }
  190. if (ifSafari && version >= ) { // 判断safari版本 如果大于9
  191. setTimeout(function() { // 必须要使用settimeout
  192. var S = document.createElement("a"); //创建a元素
  193. S.setAttribute("href", R), S.style.display = "none", document.body.appendChild(S);
  194. var T = document.createEvent("HTMLEvents"); // 返回新创建的 Event 对象,具有指定的类型。
  195. T.initEvent("click", !, !)// 初始化新事件对象的属性, S.dispatchEvent(T) // 绑定事件
  196. }, )
  197. } else {
  198. document.querySelector("#" + iframe).src = R // 将iframe增加src
  199. }
  200. var P = Date.now();
  201. setTimeout(function() {
  202. if (L) {
  203. var S = setTimeout(function() {
  204. x(P, O)
  205. }, );
  206. g.push(S)
  207. }
  208. }, )
  209. }
  210. // x方法
  211. function x(N, downUrl) {
  212. var L = Date.now();
  213. if (N && (L - N) < ( + )) {
  214. window.location.href = downUrl
  215. }
  216. }
  217.  
  218. function h(N) {
  219. var V = [];
  220. var P = N.inteneUrlParams;
  221. var T = {
  222. category: "jump",
  223. des: "productDetail"
  224. };
  225. if (N.sourceType && N.sourceValue) {
  226. T.sourceType = N.sourceType;
  227. T.sourceValue = N.sourceValue;
  228. if (P && !P.sourceType && !P.sourceValue) {
  229. P.sourceType = N.sourceType;
  230. P.sourceValue = N.sourceValue
  231. }
  232. }
  233. if (P) {
  234. for (var U in P) {
  235. if (U && P[U]) {
  236. V.push('"' + U + '":"' + P[U] + '"')
  237. }
  238. }
  239. } else {
  240. for (var U in T) {
  241. if (U && T[U]) {
  242. V.push('"' + U + '":"' + T[U] + '"')
  243. }
  244. }
  245. }
  246. try {
  247. var Q = MPing.EventSeries.getSeries();
  248. if (Q) {
  249. var W = JSON.parse(Q);
  250. W.jdv = encodeURIComponent(e("__jdv"));
  251. W.unpl = encodeURIComponent(e("unpl"));
  252. W.mt_xid = encodeURIComponent(e("mt_xid"));
  253. W.mt_subsite = encodeURIComponent(e("mt_subsite"))
  254. }
  255. var S = {
  256. mt_subsite: encodeURIComponent(e("mt_subsite")),
  257. __jdv: encodeURIComponent(e("__jdv")),
  258. unpl: encodeURIComponent(e("unpl")),
  259. __jda: encodeURIComponent(e("__jda"))
  260. };
  261. Q = JSON.stringify(W);
  262. V.push('"m_param":' + Q);
  263. V.push('"SE":' + JSON.stringify(S))
  264. } catch (R) {
  265. V.push('"m_param":null')
  266. }
  267. var M = "{" + V.join(",") + "}";
  268. var O = N.inteneUrl.split("?");
  269. var L = null;
  270. if (O.length == ) {
  271. L = O[] + "?" + O[] + "&params=" + M
  272. } else {
  273. L = O[] + "?params=" + M
  274. }
  275. return L
  276. }
  277.  
  278. function y(L) {
  279. return "intent://m.jd.com/#Intent;scheme=" + L + ";package=com.jingdong.app.mall;end"
  280. }
  281.  
  282. function n(L) {
  283. if (L.openAppBtnId) {
  284. B[L.openAppBtnId] = L;
  285. G(L.openAppBtnId, L.openAppEventId);
  286. bind(L.openAppBtnId, "click", function() {
  287. var P = this.getAttribute("id");
  288. var M = B[P];
  289. if (!t) {
  290. var N = document.createElement("iframe");
  291. N.id = iframe;
  292. document.body.appendChild(N);
  293. document.getElementById(iframe).style.display = "none";
  294. document.getElementById(iframe).style.width = "0px";
  295. document.getElementById(iframe).style.height = "0px";
  296. t = true
  297. }
  298. var O = M.cookieFlag ? "downloadAppPlugIn_downCloseDate_" + M.cookieFlag : "downloadAppPlugIn_downCloseDate";
  299. l(O, Date.now() + "_2592000000", , "/", "m.jd.com");
  300. l(O, Date.now() + "_2592000000", , "/", "m.jd.hk");
  301. openApp(M, true)
  302. })
  303. }
  304. }
  305.  
  306. function D(M) {
  307. if (M.closePanelBtnId && M.closePanelId) {
  308. B[M.closePanelBtnId] = M;
  309. G(M.closePanelBtnId, M.closePanelEventId);
  310. var Q = M.cookieFlag ? "downloadAppPlugIn_downCloseDate_" + M.cookieFlag : "downloadAppPlugIn_downCloseDate";
  311. var O = e(Q);
  312. var P = null;
  313. if (O) {
  314. P = O.split("_");
  315. if (P.length == ) {
  316. P[] = parseInt(P[], );
  317. P[] = parseInt(P[], )
  318. } else {
  319. P = null
  320. }
  321. }
  322. var L = Date.now();
  323. if (Html5Plus() || (!M.noRecord && P && P.length == && (L - P[]) < P[])) {
  324. document.querySelector("#" + M.closePanelId).style.display = "none";
  325. if (M.closeCallblack) {
  326. var N = M.closeCallblackSource ? M.closeCallblackSource : null;
  327. M.closeCallblack.call(N)
  328. }
  329. return
  330. } else {
  331. document.querySelector("#" + M.closePanelId).style.display = "block"
  332. }
  333. bind(M.closePanelBtnId, "click", function() {
  334. var U = this.getAttribute("id");
  335. var R = B[U];
  336. var T = R.cookieFlag ? "downloadAppPlugIn_downCloseDate_" + R.cookieFlag : "downloadAppPlugIn_downCloseDate";
  337. if (!R.noRecord) {
  338. l(T, Date.now() + "_259200000", , "/", "m.jd.com");
  339. l(T, Date.now() + "_259200000", , "/", "m.jd.hk")
  340. }
  341. document.querySelector("#" + R.closePanelId).style.display = "none";
  342. if (R.closeCallblack) {
  343. var S = R.closeCallblackSource ? R.closeCallblackSource : null;
  344. R.closeCallblack.call(S)
  345. }
  346. })
  347. }
  348. }
  349.  
  350. function Html5Plus() { // htmlplus
  351. if (Navigator.indexOf("Html5Plus") >= ) {
  352. return true
  353. } else {
  354. return false
  355. }
  356. }
  357.  
  358. function G(P, M) {
  359. try {
  360. var O = document.getElementById(P);
  361. var L = O.className;
  362. if (L) {
  363. L = L + " J_ping"
  364. } else {
  365. L = "J_ping"
  366. }
  367. O.className = L;
  368. O.setAttribute("report-eventid", M)
  369. } catch (N) {}
  370. }
  371.  
  372. function C(L) {
  373. var M = F(L);
  374. n(M);
  375. D(M)
  376. }
  377. Hquery.downloadAppPlugIn = C;
  378. Hquery.downloadAppPlugInOpenApp = function(L) {
  379. var M = F(L);
  380. openApp(M);
  381. }
  382. });

京东在html5页面中打开本地app的解决方案的更多相关文章

  1. 微信中通过页面(H5)直接打开本地app的解决方案

    简述 微信中通过页面直接打开app分为安卓版和IOS版,两个的实现方式是完全不同的. 安卓版实现:使用腾讯的应用宝,只要配置了“微下载”之后,打开链接腾讯会帮你判断本地是否已经安装了app,如果本地安 ...

  2. 推荐下载App,如果本地安装则直接打开本地App(Android/IOS)

    推荐下载App,如果本地安装则直接打开本地App(Android/IOS) - 纵观现在每家移动网站,打开首页的时候,都有各种各样的形式来提示你下载自身的移动App(Android/IOS),这是做移 ...

  3. web页面打开本地app(判断是否安装)

    在应用宝中有APP申请链接: //是否可以打开App不可以跳则到下载页 $(".downNow button").on("click",function(){ ...

  4. iOS开发中打开本地应用、打开appStore应用、给app评分功能实现

    app开发中,通常会有邀请用户给app打分的功能.而在iOS中,正式应用都是通过appStore 下载的,因此给app 打分也只能在 appStore中.因此,需要从应用跳转到appStore.方法是 ...

  5. iOS/Android 浏览器(h5)及微信中唤起本地APP

    在移动互联网,链接是比较重要的传播媒质,但很多时候我们又希望用户能够回到APP中,这就要求APP可以通过浏览器或在微信中被方便地唤起. 这是一个既直观又很好的用户体验,但在实现过程中会遇到各种问题: ...

  6. H5页面中唤起native app

    现在各类app,分享出去的H5页面中,一般都会带着一个立即打开的按钮,如果本地安装了app,那么就直接唤起本地的app,如果没有安装,则跳转到下载.这是一个很正常的推广和导流量的策略,最近产品经理就提 ...

  7. 给你的移动网站加点料:推荐下载App,如果本地安装则直接打开本地App(Android/IOS)

    纵观现在每家移动网站,打开首页的时候,都有各种各样的形式来提示你下载自身的移动App(Android/IOS),这是做移动客户端产品的一个很好地引流的手段.当然各家引流下载的交互和视觉各不相同,有的是 ...

  8. 学习笔记:URL Protocol在浏览器中打开本地应用程序

    看到阿里的网站上可以通过点击卖家的旺旺图标从而调用本地的阿里旺旺程序,而且还可以传递当前浏览者需要咨询的商品.这是怎么实现的呢?是通过URLProtocol来完成. 原理还没有太清楚,即在系统里注册一 ...

  9. Android之从Browser中打开本地的应用程序&微信检测是否有对应app

    在对应的应用程序的AndroidManifest.xml中配置: <activity android:name=".ui.TabHostActivity" android:w ...

随机推荐

  1. quartz 关闭不断输出的batch acquisition of 0 triggers ?

    转: spring整合quartz定时器的项目中,如何关闭不断输出的batch acquisition of 0 triggers ? 不断输出的batch acquisition of 0 trig ...

  2. #define 多行多语句

    #define DEBUG_ENABLE 1 #if DEBUG_ENABLE > 0 #define DEBUG_PORT UART_PORT2 #define DBG_BUF_LEN 512 ...

  3. Scala进阶之路-Scala中的枚举用法案例展示

    Scala进阶之路-Scala中的枚举用法案例展示 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. Scala中的枚举值和Java中的枚举值有点差别,不过使用起来也都差大同小异,我这 ...

  4. Linux操作系统原理

    Linux操作系统原理 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.计算机经历的四个时代 1.第一代: 真空管计算机,输入和输出:穿孔卡片,对计算机操作起来非常不便,做一件事 ...

  5. 函数和常用模块【day05】:迭代器(六)

    本节内容 1.简书 2.可迭代对象 3.迭代器 4.rang方法 5.总结 一.简述 我们经常使用for循环去遍历一些序列数据,但是我们有的时间发现for循环的效率很低,而且很占用了大量的硬件资源,但 ...

  6. 函数和常用模块【day05】:不同目录间进行模块调用(八)

    本节内容 1.背景 2.函数功能解释 3.绝对路径和相对路径 4.不同目录间进行模块调用 一.背景 之前写了软件开发目录规范这篇博客,相信很多人都已经知道,我们在写程序时需要遵循一定的规范,不然,就算 ...

  7. ElasticSearch 批量增加索引

    服务端批量增加索引,版本是5.1.1 TransportClient client; Settings esSettings = Settings.builder() .put("clust ...

  8. python---tornado初识(2)实现登录和发布文章

    # coding:utf8 # __author: Administrator # date: 2018/3/6 0006 # /usr/bin/env python import tornado.i ...

  9. Gym - 100269F Flight Boarding Optimization(dp+树状数组)

    原题链接 题意: 现在有n个人,s个位置和你可以划分长k个区域你可以把s个位置划分成k个区域,这样每个人坐下你的代价是该区域内,在你之前比你小的人的数量问你怎么划分这s个位置(当然,每个区域必须是连续 ...

  10. Centos7系统初始化脚本

    前言: 因公司业务增加,陆续新增服务器,时不时的来几台,手动地一台台对服务器初始化操作感觉太麻烦. 于是乎,根据初始化需求整合了一个初始化脚本,实现批量脚本初始化操作. 说明: 本脚本根据自身需求编写 ...