Let’s create a jQuery Plugin that checks user’s age when visiting your site.

As I was building my client’s new Vodka site, one of the requirements was a prompt that will ask the viewer for their date of birth. Seems simple enough right? So I started coding. Turns out that it takes a bit of work! There’s form validation, date logic, styling – so I decided to make a plugin out of it. I’m going to call it: AgeCheck.js.
View Demo
View in Github
I anticipate plenty of developers will need this kind of functionality. Now all they have to do is add a couple lines of code – and voila! Ready to start? Let’s begin.
age-check-demo

Setting up the plugin:

The code below sets up our plugin code. Basically it allows us to use the function name “ageCheck” and pass in an object (options) as parameters we can use inside our plugin.

(function ($){
    $.ageCheck = function(options) {
      //our code goes here...
     };
})(jQuery);

We then worry about what options we will allow our users to have. I suspect the minimum age will vary, they may want to have a “redirect” url, the “title” of the popup would be nice and the copy should be changeable as well.

var settings = $.extend({
    minAge : 21,
    redirectTo : '',
    title : 'Age Verification',
    copy : 'This Website requires you to be [21] years or older to enter. Please enter your Date of Birth in the fields below in order to continue:'
}, options);

The settings variable is using .extend() – which merges our options and our internal object together – allowing it to be accessible throughout our plugin code.

The Heart of our plugin

Plenty of times when writing code – I find myself re-factoring code so it’s easier to digest. The main logic of the program I put in an internal variable called “_this”. This is not accessible anywhere but inside the plugin code itself. It contains all of the variables and methods we need for our plugin to work.
The code below is our main object:

var _this = {
    month : '',
    day : '',
    year : '',
    age : '',
    errors : Array(),
    setValues : function(){
        var month = $('.month').val();
        var day = $('.day').val()
        _this.month = month;
        _this.day = day.replace(/^0+/, ''); //remove leading zero
        _this.year = $('.year').val();
    },
    validate : function(){
        _this.errors = [];
        if (/^([0-9]|[12]d|3[0-1])$/.test(_this.day) === false) {
            _this.errors.push('Day is invalid or empty');
        };
        if (/^(19|20)d{2}$/.test(_this.year) === false) {
            _this.errors.push('Year is invalid or empty');
        };
        _this.clearErrors();
        _this.displayErrors();
        return _this.errors.length < 1;
    },
    clearErrors : function(){
        $('.errors').html('');
    },
    displayErrors : function(){
        var html = '
    '; for (var i = 0; i < _this.errors.length; i++) { html += '
  • x' + _this.errors[i] + '
  • '; } html += '
'; setTimeout(function(){$('.errors').html(html)},200); }, reCenter : function (b){ b.css("top", Math.max(0, (($(window).height() - (b.outerHeight() + 150)) / 2) + $(window).scrollTop()) + "px"); b.css("left", Math.max(0, (($(window).width() - b.outerWidth()) / 2) + $(window).scrollLeft()) + "px"); }, buildHtml : function(){ var copy = settings.copy; var months = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December']; var html = ''; html += '
'; html += '
'; html += '

' + settings.title + '

'; html += '

' + copy.replace('[21]',''; html += settings.minAge+''); + '

'; html += '
'; html += '
'; html += ''; html += ''; html += '
'; $('body').append(html); $('.ac-overlay').animate({ opacity: 0.8 }, 500, function() { _this.reCenter($('.ac-container')); $('.ac-container').css({opacity: 1}) }); $(".ac-container .day, .ac-container .year").focus(function(){ $(this).removeAttr('placeholder'); }); }, setAge : function(){ _this.age = ''; var birthday = new Date(_this.year, _this.month, _this.day); var ageDifMs = Date.now() - birthday.getTime(); var ageDate = new Date(ageDifMs); // miliseconds from epoch _this.age = Math.abs(ageDate.getUTCFullYear() - 1970); }, setSessionStorage : function(key, val){ try { sessionStorage.setItem(key,val); return true; } catch (e) { return false; } }, handleSuccess : function(){ var successMsg = '

Success!

'; successMsg += '

You are now being redirected back to the application...

'; $('.ac-container').html(successMsg); setTimeout(function(){ $('.ac-container').animate({'top':'-350px'},200, function(){ $('.ac-overlay').animate({'opacity':'0'},500, function(){ if (settings.redirectTo != '') { window.location.replace(settings.redirectTo); }else{ $('.ac-overlay, .ac-container').remove(); } }); }); },2000); } }; //end _this

Our object contains everything form validation, re-centering the display, clearing errors, error and success handling and finally building the html. You can easily tell which part is which – by the names I have given the methods. This makes it easy for any programmer to come in and maintain the code.
Now that we have our settings and the main logic – it’s time to execute our code.

Chunks of Events

So first, let’s check if there’s a flag that user is old enough. If you look in our main object, we’re setting a flag if user passes our age check. We called it “ageVerified” and set it to “true”. If they are, we don’t even show the box – we just let them in the site.

if(sessionStorage.getItem("ageVerified") == "true"){
    return false;
}
_this.buildHtml();

If the flag isn’t there, we continue with our popup using our buildHtml() method.
Then the action begins when the click on our button. Below simply ties our methods to a “if, else” wrappers such as below:

$('.ac-container button').on('click', function(){
    _this.setValues();
    if (_this.validate() === true) {
        _this.setAge();
        if(_this.age >= settings.minAge){
            if(!_this.setSessionStorage("ageVerified", "true")){
                console.log('sessionStorage not supported by your browser');
            };
            _this.handleSuccess();
        }else{
            _this.errors.push('You are not old enough');
            _this.displayErrors();
        }
    }
});

So to explain, when a user click on our button – we set the values that are coming from the drop down and input fields in our form. It checks if they’re valid inputs – it they are – it sets the age we use in our plugin. Now there’s only 2 possible scenarios we set – if user is old enough or not. We handle both scenarios with letting them through or showing error messages.

Some Styles

Our plugin has to look nice. On top of that, we also don’t want it to clash with other styles of the site. We prefix all of our styles with the “ac-” to namespace our plugin classes.
The CSS for our box is right below:

@import url(http://fonts.googleapis.com/css?family=Bree+Serif);
.ac-overlay{
    box-sizing: border-box;
    height:100%;
    width:100%;
    position: fixed;
    top: 0;
    right: 0;
    bottom: 0;
    left: 0;
    background: rgba(0,0,0,0.8);
    z-index: 99998;
    opacity:0;
	cursor: wait;
}
.ac-container {
    box-sizing: border-box;
    font-family: 'Bree Serif', serif;
    width: 360px;
    position: fixed;
    padding: 20px 20px 30px 20px;
    background: #fff;
    z-index: 99999;
    opacity:0;
    text-align:center;
    border-radius: 4px;
    box-shadow:0px 0px 5px #000;
    font-weight:normal;
}
.ac-container h2 {
    box-sizing: border-box;
    margin:0 0 14px 0;
    font-size:26px;
    border-bottom:1px dashed #ccc;
    padding-bottom:11px;
}
.ac-container h3 {
    box-sizing: border-box;
    color:#649D09;
    margin-bottom:5px;
    margin-top:15px;
    font-size:26px;
}
.ac-container p {
    box-sizing: border-box;
    margin:0 0 20px 0;
    font-size: 14px;
    color:#959595;
    line-height: 20px;
}
.ac-container p strong {
    color:#FF1F1F;
}
.ac-container select,
.ac-container input {
    box-sizing: border-box;
    color:#555;
    padding: 5px 10px;
    font-size: 12px;
    line-height: 1.5;
    border-radius: 3px;
    margin-right:5px;
    border:1px solid #ccc;
}
.ac-container input.day{
    box-sizing: border-box;
    width:45px;
    height:28px;
}
.ac-container input.year{
    box-sizing: border-box;
    width:70px;
    height: 28px;
}
.ac-container select {
    box-sizing: border-box;
    height:28px;
    padding-left:4px;
}
.ac-container button {
    box-sizing: border-box;
    display: inline-block;
    margin-bottom: 0;
    font-weight: bold;
    text-align: center;
    white-space: nowrap;
    vertical-align: middle;
    -ms-touch-action: manipulation;
    touch-action: manipulation;
    cursor: pointer;
    -webkit-user-select: none;
    -moz-user-select: none;
    -ms-user-select: none;
    user-select: none;
    background-image: none;
    border: 1px solid transparent;
    border-radius: 4px;
    padding: 4px 10px 4px 10px;
    font-size: 12px;
    line-height: 1.5;
    width: 84px;
    background:#8EB908;
    color:#fff;
    text-shadow:1px 1px 0 #84A51D;
}
.ac-container button:hover{
    box-sizing: border-box;
    background:#82A711;
}
.ac-container .errors  {
    box-sizing: border-box;
    margin:0 0 20px 0;
    font-size: 12px;
    line-height: 18px;
    color:#FF1F1F;
}
.ac-container .errors ul,
.ac-container .errors li{
    box-sizing: border-box;
    padding:0 0 3px 0;
    margin:0 0 0 0;
    list-style:none;
}
.ac-container .errors li span {
    box-sizing: border-box;
    font-size:9px;
    background:#ebebeb;
    border:1px solid #ccc;
    width: 14px;
    height:14px;
    border-radius:7px;
    display:inline-block;
    color:#FF1F1F;
    font-weight: bold;
    text-align: center;
    margin-right:5px;
    line-height: 13px;
    position:relative;
    top:-2px;
    text-shadow: 1px 1px 0 #fff;
    font-family:arial;
}
.ac-container .fields {
    box-sizing: border-box;
    clear:both;
    margin:10px 0 10px 0;
}
@media (max-width: 500px) {
    .ac-container {
        box-sizing: border-box;
        width:auto;
        margin-right:20px;
        margin-left:1px;
    }
    .ac-container select,
    .ac-container input.day,
    .ac-container input.year{
        box-sizing: border-box;
        display:block;
        margin-bottom:10px;
        margin-right:0;
        width:100%;
    }
    .ac-container button {
        box-sizing: border-box;
        display: block;
        width: 100%;
    }
}

This will make our box nice and friendly. Note that I’m using Google Fonts to style it. This may not be your taste – so it’s completely optional.
Finally, let’s use the darn thing.

How to Use:

First thing you is to add the jQuery library to your page. Then download the plugin files and simply link to the .js and .css file.
Next, you initialize the plugin by calling $.ageCheck() and pass in the options if necessary. See the section “Options” below for a description of the available options and default values. You can do this inside a document ready handler.

$(document).ready(function(){
   $.ageCheck();
});

Note that if you want your whole site to be restricted, you would want this prompt to appear in any of the pages your visitor hits. So the code above will be suitable in a template used throughout the site. In your header or footer is a good spot.

Options:

You pass options in an object literal fashion. For example, if you want to redirect the user to the login page of your site on success, you would add the option like so:

$(document).ready(function(){
   $.ageCheck({
     "redirectTo" : "http://example.com/login"
   });
});

As we’ve gone through our settings above, its good to formally list the options available. Below is a nice table that does that:

Option Type Description
minAge Number Age to validate against
redirectTo URL to redirect when successful
title Title text above the prompt
copy Paragraph text below the title

Conclusion

Note that AgeCheck.js is a Javascript solution for verifying date of birth. Users can easily turn off Javascript in their browsers – to easily bypass the plugin. You may want a server side solution so the processing is done regardless of Javascript being on or off.
For more information about how to create jQuery plugins, consult their documentation.
Let me know your thoughts below:

66 Comments

  1. Hi, Michael. I am trying to create a webpage that has adult content, so I want to use your AgeCheck query. It has been a long time since I created a webpage, so I am a bit rusty. I want it to have an additional button above the “Check My Age” to say “Leave Now” that will direct the user back to their browser homepage. How would I do that?

    Reply
  2. This is the first Jquery pop up option that I have gotten to work…well sort of. I want to change the age to 18. I can change it in the css, but then it no longer makes the age stay red and it also doesn’t change the date to an 18 year old…you still have to be 21 . What am I missing to change those parameters. Thanks for a great plugin.

    Reply
  3. Nice, elegant useful code. Is there a way to modify this to have the entire overlay hidden until an age has been selected? This would be very useful where different countries have different ages to select.

    Reply
      • I was thinking more along the lines of having ac-overlay & ac-container hidden until a country selector (value=age) is selected and the continue button clicked. This would trigger an onClick to show the hidden ac-overlay & ac-container with the age [value] passed from the .minAge.
        I am assuming it requires a two step process since .on (change) doesn’t seem to recognize when ‘select’ actually changes so it needs the button click.
        I have tried using $(#ac-container’).hide(); and .show(); with no luck…
        I have also tried adding display:none; to each agecheck.css entry… Great for hiding the overlays but I can’t figure out how to change the css back to show via the onClick.

        Reply
          • That’s not working for me… now the overlays don’t show on page load but they don’t show on the button click either.
            The only change I made to your html page was:
            Canada
            United States
            So everything works fine there… the proper var minAge gets created and the [21] changes depending on selection. I just need to remove that first time the overlays pop up when the page first loads… any other thoughts on how to make a user select a country and click before the ageCheck overlay comes in?

  4. This is great. Thanks for sharing.
    How long does the age verification “stick” for? Is a cookie used? How do I specify how frequently a visitor needs to re-identify their age?
    Tanks again,

    Reply
  5. I’m getting a “true” stored in my session for the ageVerified Key but instead of getting sent to my redirect, it’s getting sent to the same url but with a ? at the end of it. Any ideas what could be wrong?

    Reply
  6. Need help please, I have no idea how to make this work it keeps saying this site requires you to be 0 years of age and I have no Idea how to edit the script

    Reply
  7. This is great, thanks. I have a question regarding storing cookies. Is there a way to easily use local storage instead and set it to one day? And if so, is there a way to check if the cookie is present? I am using wordpress and ideally when the user verifies their age, they get a cookies that lasts a day, and each page checks if the cookie is there, and if not redirects them to the home page. Thank you.

    Reply
  8. I have been testing this on my Opencart install and found that the session data works to confirm verification was done till I open the cart when it asked that I reconfirm age. I have tried to add the links in the header,footer and even the main.tpl with the same result. I am not sure if its just my browser and plugins, even so I would like to figure out what the issue is so any assitance would be appreciated

    Reply
      • Yes, its when I click on the Cart in the Header that is does it. I have not tried to recreate it but will give it a bash now. Thanks for the reply though!

        Reply
    • If it’s the same url, then it may be that the application is clearing the session key that the plugin uses. See when the page loads – it looks for that key if the visitor is “ageVerified”. The key is pretty unique though, so i would have to see it to debug it.

      Reply
  9. Hello Michael, thank you so much for creating this! It helps so much with SEO.
    Is there a way to keep the “not old enough” popup when the browser tab is refreshed? I understand if the browser window closes you have no control, but is there additional code I could add that checks for “fails” and keeps them as fails (so that minors cannot game the system).

    Reply
  10. Hi Michael,
    Thanks for this. Nice work!
    Having a problem getting this going on other site pages.
    Site Structure is:
    /index.html(landing page with agecheck.css & jquery.agecheck.js) – Works Fine here! (All Browsers)
    /Home/index.html (page with proper paths to .css & .js for the above) but does not work!
    /About/index.html (same as Home)
    /Products/index.html (same)
    /Contact/index.html (same)
    Going straight to any url but the root agecheck does not work even though no input was made on the root. Tried in FF, IE and Chrome – All work to root url, but not to inside pages.
    on /index.html paths are:
    on /Home/index.html and other internals, paths are:
    These are correct as style.css and other .js follow the same paths and work correctly.
    Please note this is under development so url = http://www.greenhillvape.com/wip/
    A little help please.
    Thanks!

    Reply
  11. Hello,
    Would it be okay if I could share this to my blog I am currently working on.
    The article I am creating a version of this is for Weebly. Wherein, I simplified the
    process they’ll undergo just to install this code to their Weebly website.
    Anyway, I also modified the CSS since the original code looks pretty obsolete 😛
    Preview:http://rt-project3.weebly.com/
    My Blog: gabrielrevilla.weebly.com
    Look forward to your reply,
    Gabriel R

    Reply
  12. Hi there Michael. Thanks for the plugin, it works well.
    One glitch I have found (possibly related to my associated coding) is that it sometimes comes up again as though the age confirm has timed out. How do I go about modifying the JS to adjust the timeout? I can see under handleSuccess() a timeout of 2000 – is this 2000 seconds?? Would this be the right value to change? Thanks!

    Reply
  13. Michael,
    How can a ‘bailout’ or ‘Exit’ button be added to your script so it does not get hung-up on the missing invalid month/day/year entry? I’ve added a new button (next to the submit button) so users can Exit the age verification (they will be directed to http://www.disney.com/) all together. However, the screen is getting stuck on the wrong Month/Day/Year screen and not going to the redirect. Thanks in advance for any assistance you can provide.

    Reply
  14. I added this to a site and it works on most pages. I do have an issue on certain pages that I believe is caused by other code interfering with the age gate code. On the broken pages it says ‘DisplayOrder’ where the bold ’21’ should be. When I input a birthday that is over 21 it denies the user from going further.
    Any insight of why this is happening and how to fix?

    Reply

Leave a Reply to Akrep Scorpion Cancel reply