Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
206 views
in Technique[技术] by (71.8m points)

javascript - How to close a progressive web app

Once a pwa is installed a mobile device, how do I close the application like a native app without having the user click the back button multiple times.

I understand it's a bad idea to window.close on a web page but this a pwa on a mobile device.

In Cordova you will use navigator.app.exitApp, this is of course not available on pwa.

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

This is a solution I created today.

When you click the back button a dialog will request you to just click the back button one more time to actually close the app, or Cancel to go back to the page.

The whole thing uses some manipulation of the history, and it works on Chrome. One could adjust things so that it would work for more browsers. It seems browsers often have slightly different philosophies when it comes to how the history should work in detail.

Therefore, in this example, I have a condition that it does its thing only for Android with Chrome, as you can see in the code.

There is also a condition of fullscreen (my PWA runs in fullscreen) so that this logic will not be used in normal browser (you can test in normal browser by setting testInBrowser = true).

closePWA.js

var testInBrowser = false; // set this to true to test in browser
if (   testInBrowser 
    || 
       /Android/i.test(navigator.userAgent)
    && /Chrome/i.test(navigator.userAgent)
    && window.matchMedia('(display-mode: fullscreen)').matches
    ) {
  if (getCookie('closing') == "true") {
    setCookie('closing', '', 1);
    showCloseDialog();
    returnToOriginalPageIfUserCancels();
    window.stop();
  } else {
    history.pushState(null, null);
    window.addEventListener('popstate', function () {
      setCookie('closing', 'true', 10);
      history.go(-(history.length - 2));
    })
  }
}
function showCloseDialog() {
  document.body.style='background-color:#aaa;';
  var s = document.createElement('SPAN');
  s.style='border-radius:10px;padding:50px 30px 40px 30px;display:table;position:absolute;left:50%;top:50%;transform:translate(-50%,-50%);text-align:center;background-color:#fff;font-size:30px;font-family:arial;font-weight:bold;';
  s.appendChild(document.createTextNode('Click back button again to close'));
  s.appendChild(document.createElement('BR'));
  s.appendChild(document.createElement('BR'));
  s.appendChild(document.createTextNode('or'));
  s.appendChild(document.createElement('BR'));
  s.appendChild(document.createElement('BR'));
  var b = document.createElement('BUTTON');
  b.style='font-size:30px;font-family:arial;background:none!important;border:none;color:blue;font-weight:bold;';
  b.innerHTML='Cancel'
  b.addEventListener('click',function(){outsideResolve()});
  s.appendChild(b);
  s.appendChild(document.createElement('BR'));
  s.appendChild(document.createElement('BR'));
  s.appendChild(document.createTextNode('to go back'));
  document.body.appendChild(s);
}
async function returnToOriginalPageIfUserCancels() {
  await new Promise(function(resolve) {
    outsideResolve = resolve;
  });
  var steps = history.length - 1;
  if (steps==1) steps=0; // takes care of the case when user clicks back on first page
  history.go(steps);
}
function setCookie(cname, cvalue, exseconds) {
  var d = new Date();
  d.setTime(d.getTime() + (exseconds * 1000));
  var expires = "expires=" + d.toGMTString();
  document.cookie = cname + "=" + cvalue + ";" + expires + ";path=/";
}
function getCookie(cname) {
  var name = cname + "=";
  var decodedCookie = decodeURIComponent(document.cookie);
  var ca = decodedCookie.split(';');
  for (var i = 0; i < ca.length; i++) {
    var c = ca[i];
    while (c.charAt(0) == ' ') {
      c = c.substring(1);
    }
    if (c.indexOf(name) == 0) {
      return c.substring(name.length, c.length);
    }
  }
  return "";
}

The script should be added right at the beginning of the body element, or very early in the body. This is important. It will not work if added in header, and it will not work properly if added later on in the body, or at the end of the body.

This should be done in every page of your PWA.

Like this:

<!DOCTYPE html>
<html>
<head>
  <title>A page</title>
</head>
<body>
  <script src="/script/closePWA.js"></script>
  <h3>A page</h3>
  <a href="anotherPage.html">Go to another page</a>
</body>
</html>

You can test it here

2020-08-17: Update: It seems that Chrome in newer versions has changed the way the history works. So the code must be adjusted. That's of course typical when not using standard solutions and inventing something like this. It shouldn't be difficult, but it requires some investigation. Hopefully the history functionality in Chrome will stabilize after some time so that this script will not have to be adjusted all the time. Right now when I tested it, it actually works in the Firefox browser.

Demo PWA (You must open in a new window / tab and with Chrome Firefox [see update above] if you want to test in browser.)

This demo is configured with testInBrowser = true so that you can test it in your normal Chrome browser. It will of course not close the browser, but you can see how the history jumps.

If you want to test it in your normal (Chrome) browser it is important that you open it in a new window / tab, because the idea is that there must not be any earlier page in the history.

The real test is to try it in your Android device with Chrome. Open it in your Chrome browser, press the tripple dots menu, select "Add shortcut on homescreen".

When the Progressive Web App is installed, start it and navigate back and forth between page 1 and 2 for a while, and then test the whole thing by pressing the back button of your device.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
...