渐进式Web应用程序上的Android后退按钮关闭de App

时间:2017-04-10 17:41:56

标签: android html5 service-worker progressive-web-apps

“纯”HTML5 / Javascript(累进式)Web应用程序是否可以拦截移动设备后退按钮以避免App退出?

这个问题与this one类似,但我想知道是否有可能在不依赖PhoneGap / Ionic或Cordova的情况下实现此类行为。

5 个答案:

答案 0 :(得分:14)

虽然无法从渐进式Web应用程序上下文中直接连接到android后退按钮,但我们可以使用历史API来实现您想要的结果。

首先,当用户所在页面没有浏览器历史记录时,按后退按钮会立即关闭应用程序。
我们可以通过在首次打开应用时添加以前的历史记录状态来阻止这种情况:

window.addEventListener('load', function() {
  window.history.pushState({}, '')
})

可以在mdn上找到此功能的文档:

  

pushState()有三个参数:一个状态对象,一个标题(当前被忽略),以及(可选)一个URL [...]如果没有指定,它被设置为该文件的当前网址。

所以现在用户必须按两次后退按钮。一次按下将我们带回原始历史状态,下一次按下关闭应用程序。

第二部分是我们挂钩窗口的popstate事件,只要浏览器通过用户操作在历史记录中向后或向前导航(当我们调用history.pushState时就不会这样)。

  

每次活动历史记录条目在同一文档的两个历史记录条目之间发生更改时,就会将popstate事件分派到窗口。

现在我们有:

window.addEventListener('load', function() {
  window.history.pushState({}, '')
})

window.addEventListener('popstate', function() {
  window.history.pushState({}, '')
})

加载页面后,我们会立即创建一个新的历史记录条目,并且每次用户按下“返回”状态。要转到第一个条目,我们再次添加新条目!

当然,这种解决方案对于没有路由的单页应用程序来说非常简单。它必须适用于已使用历史记录API的应用程序,以使当前网址与用户导航的位置保持同步。

为此,我们将为历史记录的状态对象添加一个标识符。这将使我们能够利用popstate事件的以下方面:

  

如果激活的历史记录条目是通过调用history.pushState()创建的,则popstate事件的state属性包含历史记录条目的状态对象的副本。

所以现在在我们的popstate处理程序中,我们可以区分我们用来阻止后退按钮关闭应用程序行为的历史记录条目与用于在应用程序内进行路由的历史记录条目,并且只重新推送我们的具体弹出后的预防性历史记录:

window.addEventListener('load', function() {
  window.history.pushState({ noBackExitsApp: true }, '')
})

window.addEventListener('popstate', function(event) {
  if (event.state && event.state.noBackExitsApp) {
    window.history.pushState({ noBackExitsApp: true }, '')
  }
})

最后观察到的行为是,当按下后退按钮时,我们要么返回我们的渐进式网络应用程序路由器的历史记录,要么我们保持在应用程序打开时看到的第一页。

答案 1 :(得分:5)

@alecdwm,这是纯粹的天才!

它不仅适用于Android(在Chrome和三星浏览器中),它还可以在桌面Web浏览器中使用。我在Windows上对Chrome,Firefox和Edge进行了测试,结果可能与Mac上的结果相同。我没有测试IE,因为eew。即使您主要为没有后退按钮的iOS设备进行设计,确保Android(以及Windows Mobile ... awww ...糟糕的Windows Mobile)后退按钮的处理仍然是一个好主意因此PWA更像是一个本机应用程序。

将事件监听器附加到load事件并不适合我,所以我只是作弊并将其添加到我已经拥有的现有window.onload init函数中。

请记住,在将其作为标准网页浏览时导航到您的PWA之前,它可能会让实际想要真正回到他们正在查看的任何网页的用户感到沮丧。在这种情况下,您可以添加一个计数器,如果用户两次回击,您实际上可以允许"正常"回事件发生(或允许应用关闭)。

Android上的Chrome也(由于某种原因)添加了一个额外的空状态历史状态,因此需要额外的一个回来实际返回。如果有人对此有任何见解,我很想知道原因。

这是我的反挫折代码:

var backPresses = 0;
var isAndroid = navigator.userAgent.toLowerCase().indexOf("android") > -1;
var maxBackPresses = 2;
function handleBackButton(init) {
    if (init !== true)
        backPresses++;
    if ((!isAndroid && backPresses >= maxBackPresses) ||
    (isAndroid && backPresses >= maxBackPresses - 1)) {
        window.history.back();
    else
        window.history.pushState({}, '');
}
function setupWindowHistoryTricks() {
    handleBackButton(true);
    window.addEventListener('popstate', handleBackButton);
}

答案 2 :(得分:0)

此方法对现有答案有一些改进:

允许用户在2秒内按两次回退:最佳持续时间值得商but,但是允许替代选项的想法在Android应用程序中很常见,因此通常是正确的方法。< / p>

仅在独立(PWA)模式下启用此行为:这可确保网站在用户使用Android Web浏览器时保持其预期的行为,并且仅在用户看到该网站时应用此变通办法呈现为“真实应用”。

%>%

答案 3 :(得分:0)

我不想在React应用程序中使用本机javascript函数来处理此问题,所以我搜寻了解决方案 使用react-routerreact-dom-router,但最后,在截止日期之前,本机js是 什么起作用了。在componentDidMount()内部添加了以下侦听器并设置了历史记录 进入空状态

window.addEventListener('load', function() {
  window.history.pushState({}, '')
})

window.addEventListener('popstate', function() {
  window.history.pushState({}, '')
})

这在浏览器上工作正常,但在移动设备上的PWA中仍然无法使用 最终,一位同事发现,通过代码触发历史动作是通过某种方式启动了侦听器的 和瞧!一切都准备就绪

window.history.pushState(null, null, window.location.href); 
window.history.back(); 
window.history.forward(); 

答案 4 :(得分:0)

就我而言,我在该页面上有一个带有不同抽屉的 SPA,我希望它们在用户点击后退按钮时关闭。 您可以在下图中看到不同的抽屉: SPA with drawers

我在一个中央位置(全局状态)管理所有抽屉的状态(例如打开或关闭),

我将以下代码添加到 useEffect 挂钩中,该挂钩仅在加载 Web 应用程序时运行一次

// pusing initial state on loading
window.history.pushState(
        {                   // Initial states of drawers
          bottomDrawer,
          todoDetailDrawer,
          rightDrawer,
        },
        ""
      );

      window.addEventListener("popstate", function () {
        //dispatch to previous drawer states
        // dispatch will run when window.history.back() is executed
        dispatch({
          type: "POP_STATE",
        });
      });

这是我的派遣“POP_STATE”正在做的事情,

if (window.history.state !== null) {
        const {
          bottomDrawer,
          rightDrawer,
          todoDetailDrawer,
        } = window.history.state; // <- retriving state from window.history
        return { // <- setting the states
          ...state,
          bottomDrawer,
          rightDrawer,
          todoDetailDrawer,
        };

它是从 window.history 检索抽屉的最后状态并将其设置为当前状态

现在是最后一部分,当我调用 window.history.pushState({//object with current state}, "title", "url eg /RightDrawer") 和 window.history.back()< /strong>

很简单, window.history.pushState({//object with current state}, "title", "url eg /RightDrawer")每次打开抽屉的 onClick

& window.history.back()关闭抽屉的每个动作上。