Website offline beschikbaar maken met een submit fallback

Door 15 november 2012Tips & Trucs

Een website met formulier offline beschikbaar maken en de ingevoerde data verzenden wanneer weer online, dat kan middels HTML5 i.c.m. Javascript! Om de pagina offline beschikbaar te maken kan je gebruik maken van de standaard cache methodes. Nadeel hiervan is dat wanneer je de pagina opnieuw laad je toch ineens een foutpagina krijgt. Beter gebruik je een cache manifest (werkt in alle browsers en helaas, zoals gewoonlijk, weer niet in Internet Explorer). Een voorbeeld pagina met een formulier (index.php) waarvan de inhoud simpelweg naar een tekst bestand geschreven wordt:

<?php
//Form "gesubmit"?
if($_POST)
{
	//Velden scheiden door een komma en achteraan data.txt toevoegen
	file_put_contents('data.txt', implode(', ',$_POST)."\n", FILE_APPEND);

	//Pagina opnieuw laden
	header('Location: index.php');
}
?>
<!DOCTYPE html>
<html>
<head>
	<meta charset="utf-8">
	<title>Pagina naam</title>
</head>
<body>

	<form method="post" action="">

		<input type="text" name="inputname1" />
		<input type="text" name="inputname2" />

		<input type="submit" value="Opslaan!" />

	</form>

	<p>Data:</p>
	<hr />
	<?php
	//Gegevens ophalen uit data.txt, om XSS te voorkomen door htmlspecialchars halen en als laatste middels nl2br ervoor zorgen dat de "enters" ofwel nieuwe lijnen/regels zichtbaar zijn.
	echo nl2br(htmlspecialchars(file_get_contents('data.txt')));
	?>

</body>
</html>

Manifest bestand

Een cache manifest daaraan toevoegen, simpelweg de html tag vervangen door:

<html manifest="manifest.php">

Het cache manifest maken (manifest.php):

CACHE MANIFEST
index.php

En klaar! De pagina is nu offline beschikbaar (mits deze al eenmalig bezocht is). Nadeel nu is dat wanneer de pagina (of data.txt veranderd), laten we zeggen dat er een input veld bij komt, wordt dit niet zichtbaar omdat de pagina altijd vanuit de cache geladen wordt. Wanneer wordt de pagina opnieuw opgehaald? Enkel als het manifest bestand veranderd. Dus zorgen we ervoor dat het manifest bestand veranderd als index.php en data.txt veranderen door bijvoorbeeld een md5 hash van de bestanden te maken, zodra de index.php of data.txt veranderd zal die hash ook veranderen. Laten we er een stukje commentaar in zetten met die hash.

CACHE MANIFEST
index.php
# data.txt md5 hash: <?=md5_file('data.txt');?>
# index.php md5 hash: <?=md5_file('index.php');?>

Localstorage als cache

Top! Probleem opgelost! Maar als we het formulier “submitten” wanneer offline zal er niet veel gebeuren. Wat zijn de mogelijkheden om data tijdelijk bij de bezoeker op te slaan? Cookies! Prachtig maar veel gegevens kunnen we er niet in kwijt. WebSQL, wordt beperkt ondersteund en zoals bij W3C te lezen niet meer door ontwikkeld. IndexedDB, helaas ook beperkt ondersteund. Houden we local storage over! Wordt zelfs ondersteund door Internet Explorer (8+)! En… de code in! Laten we voor het gemak jQuery erbij pakken en eerst de form submit (wanneer offline) afvangen en de gegevens in localstorage stoppen:

<script type="text/javascript" src="http://code.jquery.com/jquery-1.8.2.min.js"></script>
<script type="text/javascript">
	//Naamloze functie maken
	(function()
	{
		//Form gesubmit?
		$('form').submit(function()
		{
			//Offline?
			if( ! navigator.onLine )
			{
				//De bezoeker vertellen wat er gebeurd
				alert('Je bent niet verbonden met het internet, de gegevens worden tijdelijk lokaal opgeslagen tot je weer verbonden bent met het internet.')

				//Nieuw item met alle ingevoerde gegevens uit het form aan localstorage toevoegen
				localStorage.setItem(new Date().getTime(), $(this).serialize() );

				//Alle ingevoerde velden leeg maken
				$('input[type=text]').val('');

				//Zorgen dat het form niet echt gesubmit wordt
				return false;
			}
		});

	//Naamloze functie direct uitvoeren
	})();
</script>

Vervolgens moeten we ervoor zorgen dat wanneer er iets in localstorage zit dit de volgende keer verzonden wordt. Gezien dit allemaal javascript is en we dit middels ajax gaan doen eerst het “ajax afhandel script” maken, save.php:

<?php
if($_POST){
	//Index echo'en zodat we deze kunnen verwijderen uit localstorage
	echo $_POST['index'];
	unset($_POST['index']);
	file_put_contents('data.txt', implode(', ',$_POST)."\n", FILE_APPEND);
}
?>

En weer door met javascript:

//Online?
if( navigator.onLine )
{
	//Zijn er items in localstorage?
	if(localStorage.length)
	{
		//Door alle items in localstorage "loopen"
		for(var i in localStorage)
		{
			//Item versturen naar save.php
			$.ajax({
				url: 'save.php',
				data: 'index=' + i + '&' + localStorage.getItem(i),
				type: 'POST',
				success: function(data){
					//Wanneer succesvol verzonden verwijderen uit localstorage
					localStorage.removeItem(data);
				}
			});
		}
	}
}

En klaar! Maar zoals je wellicht gemerkt hebt dien je eerst de pagina twee maal opnieuw te laten voordat de cache overschreven wordt. Om dit probleem op te lossen een kleine “hotfix” of “hack” om het zo maar te noemen:

//Manifest bestand veranderd? (en dus index.php of data.txt)
window.applicationCache.addEventListener('updateready', function(e)
{
	if (window.applicationCache.status == window.applicationCache.UPDATEREADY)
	{
		//Cash verwijderen en nieuwe items laden
		window.applicationCache.swapCache();

		//Pagina opnieuw laden
		window.location.reload();
	}
}, false);

Best leuk dit! Waar kan je dit toepassen? Het is erg handig voor mobiele websites, komt wel eens voor dat de bezoeker geen internet heeft of dat het internet langzaam is (hopen dat het straks met 4G sneller gaat). Dan is het wel zo makkelijk dat de website bezocht kan worden én de ingevoerde gegevens verwerkt worden!

Klik hier voor een online demo