Categories
How To

Caching Dismissible Alerts With localStorage

Using Javascript loclaStorage to store persistent string data and have it keep alerts dismissed.

There are a lot of reasons to want to have a temporary, but dismissible, alert on the front end of your website. And there are a million different ways to do that. The trick is … how do you do that and make your site remain cacheable?

Web Storage (Maybe) Is the Answer Web Storage (Maybe) Is the Answer

I preface this with a warning. I’m going to be using Local Storage, and that is not something I generally advocate. Web storage (aka DOM storage) is the practice of storing data in the browser. You can do this in a few other ways, such as sessions or cookies. But both of those can be detrimental to caching. After all, PHP Sessions and cookies tell systems like Varnish and Nginx “Don’t cache this.” They’re also persistent, so if I get a cookie once, I keep it until it expires or I delete it.

On the other hand, web storage is outside the website entirely, that is my server has no idea about them, and it doesn’t really impact caching at all. Varnish, Nginx, and all those sorts of server-based services don’t care about it, and if implemented properly, there’s no problem. You can store the data locally or per-session, which means ‘forever’ or ‘for as long as this browser window is open.’

Top ↑

Don’t Use localStorage (Most of the Time) Don’t Use localStorage (Most of the Time)

That all sounded awesome. So then why are there so many articles advocating you not use localStorage? Well there are some massive caveats:

  1. It’s pure javascript, so PHP doesn’t easily get it
  2. If you store sensitive data in it, you’re a numpty
  3. It can ‘only’ store 5 megs
  4. The content has no expiration
  5. You can only use string data
  6. All your localStorage is loaded on every single page load
  7. Any other javascript can see it and play with it

That’s starting to sound squidgy, right? Randall Degges has a good writeup of the drawbacks.

Well good news here. This use is possibly the only time I’ll ever advocate it’s use. I’m going to us it here because it works with caching, it works with most browsers (IE8+), and the worst case scenario here is that people will always see my alert.

Top ↑

Pull Up With Bootstrap Pull Up With Bootstrap

I’m using Bootstrap on this particular site, and it makes my life hella easy because they built in dismissible alerts. But the gotcha? Those alerts aren’t persistent. Which means if I want an alert to go away forever, then I’m SOL.

Except I’m not. Bootstrap includes a way to run an ‘action’ on dismiss, which means I could do something like this:

jQuery(document).ready(function($) {
    var gdpr = localStorage.getItem('gdpr-alerted') || '';

    if (gdpr = 'yes') {
        $('#myAlert').alert('close');
    }

    $('#myAlert').on('closed.bs.alert', function () {
       localStorage.setItem('gdpr-alerted','yes');
    })
}

What this does is it sets a variable (gdpr) based on the content of my item in localStorage (gdpr-alerted). If that value is yes, it closes the alert. Otherwise, it sets it to yes when someone closes the alert.

That actually works just fine, but it has a weird blip if the javascript is loaded in the footer, where you would see my alert for a split second before the page finished loading. So I decided to go another way, and factor in some expirations.

Top ↑

First up the PHP:

function my_gdpr_footer(){
	if ( !is_user_logged_in() ) {
		?>
		<div id="GDPRAlert" class="alert alert-info alert-dismissible fade collapse alert-gdpr" role="alert">
			This site uses cookies for stuff. Keep browsing and we keep tracking. Wanna know more? <a href="/terms-of-use">Read our Terms of Use</a>.
			<button type="button" class="close" data-dismiss="alert" aria-label="Close"><span aria-hidden="true">&times;</span></button>
		</div>
		<?php
	}
}
add_action( 'wp_footer', 'my_gdpr_footer', 5 );

function my_scripts() {
	wp_enqueue_script( 'bootstrap', get_template_directory_uri() . '/inc/bootstrap/js/bootstrap.min.js', array( 'jquery' ), '4.1.1', 'all', true );
	wp_enqueue_script( 'lwtv-gdpr', get_template_directory_uri() . '/inc/js/gdpr.js', array( 'bootstrap' ), '1.0', 'all', true );
}
add_action( 'wp_enqueue_scripts', 'my_scripts' );

Now my sneaky JS:

// jQuery dismissable - sets localStorage a year

jQuery(document).ready(function($) {
	var gdpr = localStorage.getItem('gdpr-alerted') || '';
	var year = new Date().setFullYear(new Date().getFullYear() + 1);

	if (gdpr < new Date()) {
		$('#GDPRAlert').addClass('show');
		localStorage.removeItem('gdpr-alerted');
	}

	$('#GDPRAlert').on('closed.bs.alert', function () {
		localStorage.setItem('gdpr-alerted',year);
	})
});

The reason this works is that I remove the show class from the PHP and by default assume it shouldn’t show. Then in the javascript, I set up the value as ‘a year from now’ instead of ‘yes’, and check. If the localStorage value is less than ‘now’, then it’s expired and we should show the class (and delete the storage). Otherwise, it’s the same old same old.

Top ↑

But Should We? But Should We?

That’s the big question though. Is this the best way to go about it? The next best choice would be IndexedDB, however that is not supported by ‘enough’ browsers yet, so yes. For now, this is the best choice.

2 replies on “Caching Dismissible Alerts With localStorage”

In this case, since the worst outcome is you have to click on every page because either you’re on really old IE or disabled a whole mess of stuff (which would break the rest of the web at this point) I’m not terribly worried about it.

Comments are closed.