One thing I hear often when working on web development projects is: “It used to work!” This past week I had the opportunity to tackle an “It used to work” project, and this is a review of that project.
Details
The Pretty Puppy Association (PPA)* has a website for their members and other puppy enthusiasts. It is built in Expression Engine and they are using the Low Reorder add-on (among others) and jQuery.
One of their channels is State Puppies. It has 51 entries, each one featuring an official state puppy for each state in the U.S. (and one for Puerto Rico). Each entry has a photo, a breed name, and basic information about the puppy’s breed.
*This is entirely made up, but ultimately the subject matter of the website doesn’t matter for this post.
It Used to Work Feature
On the PPA website homepage, there is a Spotlight on Spot section where the image and breed name of the featured puppy is displayed. Here users can select their state’s official puppy from a dropdown menu. When they make a selection, the photo and breed name in the spotlight section change to match their selection. When users visit the website in the future, their last-selected puppy should be seen in the spotlight feature.
The code
In the homepage template, we have the spotlight section itself, including a form with only a select element. You'll notice everywhere there would normally be puppy-specific info (breed, image URL) there is...nothing. That information is being retrieved and placed in the DOM via JavaScript.
The select element itself is being populated via Expression Engine, with the help of the Low Reorder add-on.
<div class="section-4 puppysection">
<div class="row">
<div class="container">
<h2 class="col-md-12 spotlighthdr"><i class="fa fa-chevron-circle-right" aria-hidden="true"></i> Spotlight On Spot</span></h2>
<div class="col-md-4">
<div class="homefeatured">
<a class="puppylink" href="URL"><div id="divFeaturedPuppyBreed" class="featuredbreed">Breed</div></a>
<a id="aFeaturedPuppy" href="URL"><img id="imgFeaturedPuppy" src="IMG" class="img-responsive puppy-photo" alt="BREED" width="326" height="336" /></a>
</div>
</div>
<div class="col-md-8">
{exp:channel:entries channel="new_home_page" dynamic="off" limit="1"}
{spotlight_text}
{/exp:channel:entries}
<div class="col-md-8">
<form class="connectform">
<div class="col-md-8 nopadding">
<label title="Pick a state to see that state's official state puppy and get a link to read more." for="drpPuppies" class="hiddenLabel2">Pick a state to see that state's official state puppy and get a link to read more.</label>
<select id="drpPuppies" name="puppylist" form="puppyform">
{exp:low_reorder:entries set="puppies" dynamic="no" disable="member_data|category_fields"}
<option value="{entry_id}">{state}</option>
{/exp:low_reorder:entries}
</select>
</div>
</form>
<div style="clear:both;padding-top:20px;">
<a id="aFeaturedPuppyLink" href="URL">Read more about the <span class="sbreed"></span></a>
</div>
</div>
</div>
</div>
</div>
</div>
We also have some inline JavaScript in the homepage template. There is a change event listener set on the state select. When a change event is triggered, JSON data is retrieved from "/site/puppy-info/VALUE_OF_SELECTION". If we have a successful GET request, that information is used to populate our spotlight section with the selected puppy's info.
At the very bottom of the script, we see $('#drpPuppies').change();
. This line of code is triggering the change event, without a user actually making a selection. This will become important later one.
function jqExt() {
$('#drpPuppies').on('change', function () {
$.getJSON("/site/puppy-info/" + this.value, function (result, status) {
if (status == "success") {
$('#divFeaturedPuppyName').text(result.name);
$('.sname').text(result.name);
$('.puppylink').attr("href", result.bio_url);
$('#aFeaturedPuppy').attr("href", result.bio_url);
$('#aFeaturedPuppyLink').attr("href", result.bio_url);
$('#imgFeaturedPuppy').attr("alt", result.name);
$('#imgFeaturedPuppy').attr("width", result.image_width);
$('#imgFeaturedPuppy').attr("height", result.image_height);
$('#imgFeaturedPuppy').attr("src", result.image_url);
}
});
});
$('#drpPuppies').change();
}
Let's look at '/site/puppy-info/'
{exp:low_reorder:entries set="puppies" entry_id="{segment_3}" }
{exp:ce_img:pair src="{puppy_picture}" width="300" height="340" crop="yes|center,top|0,0|yes" allow_scale_larger="yes"}
<?php
setcookie("exp_custom_default-puppy-id", {entry_id}, time() + (86400 * 365), "/", $_SERVER['SERVER_NAME']);
header("Content-Type: application/json");
$data = array( 'id' => '{entry_id}', 'breed' => '{breed}', 'image_url' => '{made}', 'image_width' => {width}, 'image_height' => {height}, 'puppy_url' => '{url_title_path="puppies/bio"}' );
echo json_encode($data);
?>
{/exp:ce_img:pair}
{/exp:low_reorder:entries}
If you blink, you might miss the setcookie() function. When users change that select dropdown, a GET request is sent to '/site/puppy-info/VALUE_SELECTED'. That value selected, or entry_id is set as a cookie.
Conceivably, when the user leaves the homepage and comes back, their last-selected puppy will be showing in the spotlight section, right? That wasn't happening.
The Problem
Instead of showing a user’s last-selected puppy, the Spotlight on Spot section is defaulting to the first state on the dropdown menu: Alabama – Blackhound. (I also made this up for example purposes. There is no official dog breed of Alabama.)
Why? First, the cookie was set, but that was it.
The cookie was never read and nothing was ever done with the value of that cookie.
Also, triggering the change event led to the featured pup always being Alabama, because of the way the select element works. The first option in the select element is always selected be default. So the page loads, the change event is triggered, this.value defaults to Alabama and the cookie value is set to Alabama. And over and over....
The Solution
Solving the problem involved four main parts: reading the cookie when the window loads; using that value in our GET request; and not triggering the change event automatically.
<script>
function getCookie(key) {
var keyValue = document.cookie.match('(^|;) ?' + key + '=([^;]*)(;|$)');
return keyValue ? keyValue[2] : null;
}
function setPuppy(result) {
$('#divFeaturedPuppyName').text(result.name);
$('.sname').text(result.name);
$('.puppylink').attr("href", result.bio_url);
$('#aFeaturedPuppy').attr("href", result.bio_url);
$('#aFeaturedPuppyLink').attr("href", result.bio_url);
$('#imgFeaturedPuppy').attr("alt", result.name); $('#imgFeaturedPuppy').attr("width", result.image_width);
$('#imgFeaturedPuppy').attr("height", result.image_height);
$('#imgFeaturedPuppy').attr("src", result.image_url);
}
window.onload = (event) => {
puppySelect = document.getElementById('drpPuppies');
let myPuppy = getCookie("exp_custom_default-puppy-id");
if(myPuppy == "" || myPuppy == null){
myPuppy = "27";
}
puppySelect.value = myPuppy;
$.getJSON("/site/puppy-info/" + myPuppy, function (result, status) {
if (status == "success") {
setPuppy(result)
}
});
};
function jqExt() {
$('#drpPuppies').on('change', function () {
$.getJSON("/site/puppy-info/" + this.value, function (result, status) {
if (status == "success") {
setPuppy(result)
}
});
});
}
</script>
First we have getCookie()
, a function that will retrieve any cookie, given its name.
Next I pulled all of the code around setting the puppy-specific info in the spotlight section into a new function setPuppy()
so I can call it in two other functions without repeating myself. It takes the results of the GET request as an argument.
Up next is a window.onload event where we are checking for the "exp_custom_default-puppy-id" cookie and saving it into the variable myPuppy. If it is not set, we are returning a default value (27 - for Florida!).
The myPuppy value is used in our GET request. This way, we are sure that every user is greeted by an adorable puppy, whether or not they've visited before or their cookie has expired. We're also using setPuppy()
to set the values of the elements we're referencing using the results of our GET request.
Last is our trusty jqExt()
function, still with its change event listener intact. This takes care of rendering the correct pup when the user makes a selection. It also calls our setPuppy() function.
Thoughts
It turns out this “It used to work” never worked. It was requested when the website was updated, but this feature never worked in the way it was requested. On a former iteration of the website, the spotlight section would be randomly populated every time the homepage loaded.
Have you worked on any "it used to work" projects lately?
Top comments (0)