Contents:
- The Case of the Responsive Resume
- The Skeleton
- Interactivity and the PDF
So, in the last two episodes we looked at why we might want to create a responsive resume, and we set up a basic one with an easy to use framework and HTML5 and CSS3 for a simple semantic layout.
Interactivity
Now, in part one I talked about why we would want to make an HTML resume in the first place, and for me the big draw is the interactivity of the web. We can make our website do things! The first thing I want to do is make your work experience searchable.
This is something I’d been planning to do, but when I saw Michael Kelly’s resume I knew he had chosen the right interface. Check it out, we’re going to do basically the same thing.
The HTML
The first place to get started is in the HTML. Remember this?
1 2 3 4 5 6 7 8 9 |
<footer> <h3>Key Skills</h3> <ul> <li>Objective-C</li> <li>Finite-State Programming</li> <li>Thai</li> <li>etc.</li> </ul> </footer> |
We did this for two reasons:
- It makes sense to use list elements to represent data that is a list
- Lists are easy to search and grab data from in javascript
If you did this, then we can dive into the Javascript. If you didn’t, go back and do it!
The jQuery
Now that you’re done, let’s look at what we’re going to do. We’re going to use javascript (jQuery) to grab all the different skills by selecting the LI
s in the footers and add in ones we don’t have already.
1 2 3 4 5 6 7 8 9 10 11 |
// set up our jQuery function $(function(){ // Make a list of all the skills var allSkills = []; $('footer.skills ul li').each(function(){ // Check if the skill is already in the list, if not, add it if (jQuery.inArray($(this).text(), allSkills) == -1) { allSkills.push($(this).text()); } }); }) |
Alright, good start, now we need to actually make the skills list and buttons. BUT WAIT, where does the skills list go? We need a div for that! Let’s add it dynamically with jQuery so that it degrades nicely and users with JS off don’t get an empty div with just a header.
Add this inside your existing $(function(){})
you just created. In fact, everything from here will go in there.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 |
// Let’s set a general variable for the easing speed for show/hide showHideDuration = 500; // First we can create the section to hold the skills // and insert it after the Experience and education section $('#exp_and_ed').after('<section id="skillsSection"><h2>Skills</h2></section>'); // add in buttons to select all or none and the UL to hold the list items // and attach a function to show/hide all as necessary $selAll = $('<span class="linkLike">All</span>').click(function(){ // Just show all the work classed articles $('.work').show(showHideDuration); // Make all the boxes true $('#skillsUL li input[type=checkbox]').attr('checked', true); }); // The inverse (converse?) of the previous… $selNone = $('<span class="linkLike">None</span>').click(function(){ $('.work').hide(showHideDuration); $('#skillsUL li input[type=checkbox]').attr('checked', false); }); // Create the UL $skillsUL = $('<ul id="skillsUL"></ul>'); // Attach everything $selAll.appendTo($('#skillsSection')); $('#skillsSection').append(' | '); $selNone.appendTo($('#skillsSection')); $skillsUL.appendTo($('#skillsSection')); // Now… // Sort and add the checkboxes, LIs, etc... allSkills.sort(); $.each(allSkills, function(index, value) { // Make a name without spaces or other characters // We'll use this as the unique checkbox name $cleanText = 'skills_'+value.replace(/\W/g, '_'); $newLI = $('<li><input type="checkbox" id="'+$cleanText+'" value="'+value+'" checked="checked" /> <label for="'+$cleanText+'">'+value+'</label></li>'); // Attach the new LI to the list. $skillsUL.append($newLI); }); |
Now, we need to think about the actual showing and hiding of article
s. We are showing and hiding based on skills. It’s easy to get the list of all the skills, but finding which article has which skills is kind of a pain in the ass. So, instead, let’s go back and add a hook class to our articles to make them easy to find.
Edit your first function that grabs all the skills so it looks like this:
1 2 3 4 5 6 7 8 9 10 |
$('footer.skills ul li').each(function(){ // Only include each skill once if (jQuery.inArray($(this).text(), allSkills) == -1) { allSkills.push($(this).text()); } // add a class hook to the article for ease of searching hook = 'skillsClass_'+$(this).text().replace(/\W/g, '_'); $(this).parents('#exp_and_ed article.work').addClass(hook); }); |
Now it’s easy to find the .work article
s which are associated to which skills by just calling $(“.skillName”)
.
Next, add the code to show/hide the articles:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
// Attach the change event handler so they hide and show articles when toggled $('#skillsSection ul li input[type=checkbox]').change(function(){ // Find all work articles with $(this).attr('value') in their class list and show/hide them $mainValue = 'skillsClass_'+$(this).attr('value').replace(/\W/g, '_'); $isChecked = $(this).attr('checked'); $('#experience .work.'+$mainValue).each(function(){ var classes = $(this).attr('class').split(/\s+/g); $isChecked ? $(this).show(showHideDuration) : $(this).hide(showHideDuration, function(){unCheck(classes)}); }); }); // When a work item is hidden, uncheck the associated boxes var unCheck = function(items) { $.each(items, function(index, item){ if (item != 'work') { theID = item.replace(/^skillsClass_/g, 'skills_'); $('#'+theID).attr('checked', false); } }); } |
If you were looking closely you may have noticed that there is also code to uncheck checkboxes of other skills that are in the same list as an item you choose to hide. This way a box will only be checked if all its members are visible.
JS: Done! It should look something like this.
Styling
Final thing, some styling. I’ll let you work it out on your own, but I recommend hiding the #skillSection div
for mobile browsers using the mobile queries in Skeleton’s layout.css
file.
Remember another we talked about back in the first article, that the resume should be easy to maintain? Well, look what we’ve done here, we’ve made it self-generate a list of skills so we don’t need to worry about them! Nifty!
Speaking of “easy to maintain”, there’s one more thing I want to talk about here.
The PDF
I’m a big fan of the PDF when it comes to resumes and CVs. I think it has a lot to offer; it’s portable, readable on a wide array of devices, easily storable. Everything a website is not (including inflexible). Remember that I said something at the beginning about keeping all the advantages of PDFs along with advantages of an easy to maintain website? Well, I recommend adding a “Download PDF” link to your online resume.
“No shit” I can hear you saying.
Yeah it’s obvious, but why not take the “easy to maintain” criterion along with you and have your website generate the PDF on the fly, so whenever you edit your website, your PDF gets updated as well? I installed wkhtmltopdf which converts HTML to PDFs using the webkit browser engine. That’s right, it will look the same as in your webkit browser. Just point it to the site, prepare a print stylesheet (like at the bottom of this one) and enjoy!
I used a PHP wrapper for wkhtmltopdf and the following code is all it took to generate a PDF file every time the resume is edited:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
// What do you want to call your PDF? $PDFName = 'Dougal_Graham_-_CV.pdf'; // What is your HTML CV called? $DVName = 'index.html'; // If the PDF file is older than the HTML file then create it again, otherwise, just redirect // Also create if the file does not exist if (file_exists($PDFName) == false || filemtime($PDFName) > filemtime($PDFName)) { include('wkhtmltopdf.php'); $pdf = new WkHtmlToPdf(array( // 'no-outline', // Make Chrome not complain // Doesn't seem to accept my @page margins in the stylesheet, define them again 'margin-top' => '2cm', 'margin-right' => '1.5cm', 'margin-bottom' => '2.5cm', 'margin-left' => '2cm', )); $pdf->setPageOptions(array( // 'disable-smart-shrinking', 'print-media-type', 'header-html' => 'blank.html', 'footer-html' => 'blank.html', 'header-spacing' => 2, 'footer-spacing' => 2 )); $pdf->addPage('index.html'); $pdf->saveAs($PDFName); } // Redirect to the PDF header('Location: '.$PDFName); |
When you’re all done, it should look something like this. Try it out and let me know how it goes.
Leave a Reply