content-personalization

Content Personalization on Squarespace

Will Strohl

11/30/2020 5:00 PM

Location Device Detection Technology Device Data Development

Imagine having the power to display a different call-to-action to visitors, based on where they are...now you can!

This is a copy of the blog article written by Will Strohl, CEO and Founder of Upendo Ventures

Squarespace has been gaining traction ever since it was first released, and for good reason. It's hands-down one of the best options for any new company to launch their first website. You don't need to be technical to create and manage your website. Anyone with any level of proficiency with a web browser can be a web hero.

In fact, even though our company, Upendo Ventures is well-known for our DNN prowess, many of our small business clients are currently on Squarespace and unless their needs change, they're going to stay there. It's the best tool for what they need today. There's a reason that Squarespace is one of the most popular point-and-click website builder solutions out there.

There is an overlap though, where Squarespace doesn't appear to be able to do many things that we'd consider to be more advanced.

Sometimes, a client's needs begin to branch out, to need to do something the "big boys" may take for granted, but otherwise are still perfectly fine staying on Squarespace. An obvious example for those who already build Squarespace websites would be adding and using third-party JavaScript libraries or additional fonts. There are what we'd consider "hacks" to do these more advanced things. One that we're going to highlight in this post is content personalization.

What is Content Personalization?

Personalized content can allow you to target website visitors in ways that raise the bar above and beyond any of your competitors. Imagine having the power to display a different call-to-action to visitors, based on where they are. For example, what if you detect that the visitor is near your location and you offer a discount code for pick-up only? In that same scenario, maybe we display a different promo since we know they're not local. In yet another, what if we wanted to display a survey, but only to visitors in specific regions? The possibilities are endless.

Content personalization is a feature that's often reserved for enterprise-level CMSs like Sitecore or Adobe Experience Manager. However, it doesn't need to be. With a tiny bit of effort, any website can offer a personalized experience for its visitors. We're going to show you how, using 51Degrees.

Anyone with a little bit of JavaScript knowledge can implement content personalization in Squarespace (and other types of sites) using 51Degrees.

51Degrees

51Degrees is known worldwide to be the fastest and most accurate mobile device detection service available to developers and website marketers. They power over 1.5 million active implementations around the world and they are adding up to 200 device definitions every week. It is a no-nonsense decision to use them for anything mobile like this. They’re available to nearly any platform you can develop on. Luckily, you don't need a brand-name budget to benefit from 51Degrees.

Create & Configure Your 51Degrees Script

If you happen to have used 51Degrees in the past, you should know that the entire getting started process has been re-imagined. There is now a super-simple wizard to help you get exactly what you need and nothing you don't, called the 51Degrees Cloud Configurator.

configurator-display

Since we have a specific example in this article of helping a small business grow its audience and convert more leads in this time of need, we're going to at a minimum choose a few of the location properties.

In this case, we're going to help a business named Urbanite Suburbanite display a different offer to visitors that are local, versus visitors that are not.

urbanite

SIDE NOTE: Urbanite Suburbanite is the most popular and trusted personal wardrobe styling service in the San Francisco Bay Area. They've prepared clients for TED talks and even once styled the original Wonder Woman, Lynda Carter.

There are so many options, by the way. If you're an electronics vendor, you can use this service to detect if the visitor is on a desktop, tablet, or mobile device. Then, you can take it a step further to see if their hardware is perhaps outdated and offer them a coupon to immediately purchase a new device. Mind-blowing!

properties-selected

Grab your pre-populated properties resource key here

For this article, we selected the properties highlighted below. The most important ones for you will be the Location properties.

  • Device > Device > IsMobile
  • Device > Device > IsTablet
  • Device > JavaScript > JavaScriptHardwareProfile
  • Operating System > Name > PlatformVendor
  • Operating System > Name > PlatformName
  • Operating System > Name > PlatformVersion
  • Web Browser and Apps > Name > BrowserVendor
  • Web Browser and Apps > Name > BrowserName
  • Web Browser and Apps > Name > BrowserVersion
  • Location > javascript > Javascript
  • Location > location > Town
  • Location > location > County
  • Location > location > Region
  • Location > location > State
  • Location > location > Country
  • Location > location > CountryCode

Now that you have all the properties selected, you can move onto the next screen by clicking the Details button. Once you've selected the 'Free' option and reviewed and accepted our Terms and Conditions you can finish the configuration by selecting the Implement button. (You should, of course, consider entering your contact information and domain name.)

review-configurator

We're going to use the client-side implementation, so be sure to copy the HTML source for reference later. Otherwise, we're done here.

client-side

Adding 51Degrees to Squarespace

To fully implement this, you're going to need to be on the Squarespace Business plan or higher (as of the time of this writing). This will allow you to add custom scripts (JavaScript) and CSS (styles) to your site and pages.

squarespace

We're most likely going to want to add this script to all pages on the site so that we can offer visitors a promotion no matter where they enter. To manage this setting, you'll want to refer to Squarespace's documentation on Site-Wide Code Injection.

Once you're viewing the header section in the site-wide code injection editor, you want to copy and paste the line of code from the HTML example you generated earlier that loads the 51Degrees script from the cloud server. It looks similar to the example below. (FYI - The file name in the path is unique to your account. Don't share it.) Depending on what you're doing in there already, you may just want to paste that below the rest of the code that may be there.

code-injection

<script async
src="https://cloud.51degrees.com/api/v4/YOUR-UNIQUE-FILE-NAME.js"></script>

The rest of the script code is not necessary to copy and paste, but it is a good example of how to work with the script yourself. The fod object is from 51Degrees and it is your gateway into fully leveraging their API for your benefit.

SPECIAL NOTE: jQuery is not loaded by Squarespace unless you configure it to do so. We'll want it for what we're about to do but including it will make it easier to do many other things in the future. You can do this right now by adding it like you would any other website, as shown below, loading from CdnJs. You usually want this to be in the top of this setting field.


<script type="text/javascript" src="//cdnjs.cloudflare.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>

Save your changes. Technically, we've already integrated 51Degrees into your Squarespace site. Though, it doesn't technically do anything yet... Congratulations!

Create the Content to Personalize

In our example of presenting a different value proposition to local and remote visitors, we can do this many different ways. One way is to completely generate and inject the content via script. To keep this example simple, we're going to do this a different way.

On the given page where you want to display the different call-to-action (CTA), create two different blocks of text with the desired content and imagery. Here you can see that there is a CTA in the header for downloading a white paper and another to use the virtual services option. The Zoom white paper will be shown to "local" visitors, while the other CTA will be shown to visitors that we don't designate as "local".

blocks

Luckily for us, Squarespace uses an underlying framework called YUI, and this makes it super-simple for us to find and target any content on the page via CSS and JavaScript based on the ID of the content. You just need to inspect the HTML source to find and note the correct ID attribute.

block-ids

Note the IDs for the content blocks you want to target for the next and final step.

Personalize the Content

Now that we have some content to promote to different people, we can personalize who gets to see which content block. There are several tricks to make this a complete use case. This is the fun part!

Our first step is to prepare the code to wire things up. The snippet below does just that, including the first variable that allows us to not execute the code during the content editing experience. We'll use that first variable to only run our personalization logic in production later.


     var $isProduction = (window.location.host == "urbsuburbstyle.com");
     var $town = "";
     var $state = "";
     var $county = "";
     var $countryCode = "";
     var divLocalVisitor = "#block-yui_3_17_2_1_1601959160530_18648"; // zoom
     var divRemoteVisitor = "#block-yui_3_17_2_1_1587141409874_16483"; // virtual
     var $divLocal = {};
     var $divRemote = {};

By the way, web browsers will hide the “www.” in your domain name, if it’s there. If you find that the code is trying to run while you’re in the administration area, you may want to add the “www.” to your domain in the snippet above.

For those of you that are not JavaScript novices, you can use the line of code below in the console to check this ahead of time.

console.log(window.location.host);

Next, we're going to use jQuery to hide both CTAs until we're ready to show one of them - but only in production.


   jQuery(document).ready(function(){
   if ($isProduction){
   $divLocal = jQuery(divLocalVisitor);
   $divRemote = jQuery(divRemoteVisitor);
   if ($divLocal != undefined) {
   $divLocal.hide();
   }
   if ($divRemote != undefined) {
   $divRemote.hide();
   }
   }
   });

Now we can run the 51Degrees code to do all the personalization magic we desire. The snippet below is the prescribed way to do this, per the 51Degrees documentation. This event runs after the entire page and its resources have all loaded, while the jQuery event above loads as soon as the page allows it.


     window.onload = function() {
       /* calling 51Degrees */
       fod.complete(function (data) {
         /* uncomment the line below to inspect the response from 51Degrees */
         /*console.log(data);*/
         $town = data.location["town"];
         $state = data.location["state"];
         $county = data.location["county"];
         $countryCode = data.location["countrycode"];
         /* only run if the page has the expected elements and is production */
         if ($isProduction && $divLocal != undefined && $divRemote != undefined){
           if (isVisitorLocal()){
             var btnText = jQuery(divLocalVisitor + " a.sqs-block-button-element:first").text();
             if ($town === "" || $town == "null"){
               // in some rare cases, the town may not be populated
               jQuery(divLocalVisitor + " a.sqs-block-button-element:first").html("We love our neighbors\n" + btnText).wrap("<pre/>");
             }
             else{
               jQuery(divLocalVisitor + " a.sqs-block-button-element:first").html("We love our " + $town + " neighbors\n" + btnText).wrap("<pre/>");
             }
             $divLocal.show();
             $divRemote.hide();
           }
           else {
             var btnText = jQuery(divRemoteVisitor + " a.sqs-block-button-element:first").text();
             if ($state === "" || $state != "null") {
               jQuery(divRemoteVisitor + " a.sqs-block-button-element:first").html("We love our neighbors\n" + btnText).wrap("<pre/>");
             }
             else{
               jQuery(divRemoteVisitor + " a.sqs-block-button-element:first").html("We love our " + $state + " neighbors\n" + btnText).wrap("<pre/>");
             }
             $divLocal.hide();
             $divRemote.show();
           }
         }
       }, 'location'); /* requires user approval to know the location */
     }

In the snippet above, an important thing to note is the additional "location" parameter. This will prompt the web browser to request permission from the visitor to know their location. (So, you will want to account for them not giving you permission in your code. The location values will be undefined if they didn't give permission to know their location.)

You may have noticed that the previous snippet makes use of a method named IsVisitorLocal(). Since the values from 51Degrees were scoped in the earliest snippet, we can easily parse the response to determine if the visitor is local or not. In this example, we're looking for folks in California, but in specific counties.


   function isVisitorLocal(){
       var isLocal = ($countryCode != undefined && $countryCode == "us" && $state != undefined && $state == "California");
       if (!isLocal) {
         return false;
       }
       var localCounties = ["San Francisco County", "San Mateo County", "Santa Clara County", "Alameda County"];
       isLocal = (localCounties.indexOf($county) > -1);
       return isLocal;
     }

If you’re putting together your code step-by-step using the instructions above, do be sure to wrap your code inside of an open <script> tag and a closing </script> tag. You’ll see this at the end of the article as well.

If you were to put this all together right now, it would work beautifully. If someone visits the site from a San Francisco Bay Area county, they'll be shown the Zoom white paper CTA. Otherwise, they'll be shown the virtual services CTA. We're going to take this one step further though...

What if we wanted to give them a friendly greeting, based on their location?

When people live near you, it's often proper to think of them and even call them your neighbor. So why not say hello in such a manner? With just a couple more lines of code, we can dynamically update the CTAs to include a friendly greeting.

For local visitors, we're saying that we love our neighbors that live in their town. Everyone else will see a message that we love our neighbors in their respective state. This is a very useful way to catch the eyes of our visitors to further engage with them.

For this to work, the visitor needs to give the script permission to know their location. The 51Degrees script does this for you. Cool, right?

allow-location

Here is the final result for a local visitor.

local-display

Final Personalization Code Snippet for Squarespace

Here is the final snippet for your convenience. We hope you enjoy it! How are you going to personalize your own Squarespace website?


   <script type="text/javascript" src="//cdnjs.cloudflare.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
   
   <script async src="https://cloud.51degrees.com/api/v4/AQQNX4to3FWGHaNp2Eg.js"></script>
   
   <script>
    var $isProduction = (window.location.host == "urbsuburbstyle.com");
    
    /* uncomment the line below to inspect the actual domain name */
    
    /*console.log(window.location.host);*/
        var $town = "";
        var $state = "";
        var $county = "";
        var $countryCode = "";
        var divLocalVisitor = "#block-yui_3_17_2_1_1601959160530_18648"; // zoom
        var divRemoteVisitor = "#block-yui_3_17_2_1_1587141409874_16483"; // virtual
        var $divLocal = {};
        var $divRemote = {};
    
    jQuery(document).ready(function(){
        if ($isProduction){
        $divLocal = jQuery(divLocalVisitor);
        $divRemote = jQuery(divRemoteVisitor);
        if ($divLocal != undefined) {
        $divLocal.hide();
    }
        if ($divRemote != undefined) {
        $divRemote.hide();
    }
    }
    });
    
    window.onload = function() {
        /* calling 51Degrees */
    fod.complete(function (data) {
        /* uncomment the line below to inspect the response from 51Degrees */
        /*console.log(data);*/
    $town = data.location["town"];
    $state = data.location["state"];
    $county = data.location["county"];
    $countryCode = data.location["countrycode"];
        /* only run if the page has the expected elements and is production */
    if ($isProduction && $divLocal != undefined && $divRemote != undefined){
    if (isVisitorLocal()){
    var btnText = jQuery(divLocalVisitor + " a.sqs-block-button-element:first").text();
    if ($town === "" || $town == "null"){
        
        // in some rare cases, the town may not be populated
    jQuery(divLocalVisitor + " a.sqs-block-button-element:first").html("We love our neighbors\n" + btnText).wrap("<pre/>");
    }
    else{
    jQuery(divLocalVisitor + " a.sqs-block-button-element:first").html("We love our " + $town + " neighbors\n" + btnText).wrap("<pre/>");
    }
    $divLocal.show();
    $divRemote.hide();
    }
    else {
    var btnText = jQuery(divRemoteVisitor + " a.sqs-block-button-element:first").text();
    if ($state === "" || $state != "null") {
    jQuery(divRemoteVisitor + " a.sqs-block-button-element:first").html("We love our neighbors\n" + btnText).wrap("<pre/>");
    }
    else{
    jQuery(divRemoteVisitor + " a.sqs-block-button-element:first").html("We love our " + $state + " neighbors\n" + btnText).wrap("<pre/>");
    }
    $divLocal.hide();
    $divRemote.show();
    }
    }
    }, 'location'); /* requires user approval to know the location */
    }
    
    function isVisitorLocal(){
    var isLocal = ($countryCode != undefined && $countryCode == "us" && $state != undefined && $state == "California");
    if (!isLocal) {
    return false;
    }
    var localCounties = ["San Francisco County", "San Mateo County", "Santa Clara County", "Alameda County"];
    isLocal = (localCounties.indexOf($county) > -1);
    return isLocal;
    }
    </script>