Javascript contextual clock

Contextual clock screenshot

Another experiment I did way back to create a contextual clock in html/css/js, originally in jQuery but now updated to vanilla javascript. Source available on Github.

The clock itself is a table of cells containing a single character, each with its own ID. Rows have letters and columns numbers.

<table>  
  <tr>
    <td id='a1'>I</td>
    <td id='a2'>T</td>
    ...

With this we can create the words and numbers as strings of IDs separated by commas. Because arrays are zero indexed I'm explicitly setting the hours so the array key matches the hour (eg. [1] = one):

var itis = "a1,a2,a4,a5";  
var a = "a7";  
var ten = "a9,a10,a11";  
...

var hourwords = new Array();  
hourwords[1] = "d1,d2,d3";  
hourwords[2] = "d4,d5,d6";  
...

Before we update the time it's important to clear the board. To do this we loop through all the <td> elements, ensure each has a numeric key and remove the CSS class 'alight':

function clearBoard(){  
  var alltds = document.getElementsByTagName('td');
  for (var index in alltds) {
    if(!isNaN(parseInt(index, 10)) && alltds[index].classList.contains('alight')){
      alltds[index].classList.remove("alight");
    }
  }
}

To determine which words to light up we create an empty array to hold the words, then use an instance of the Date object to get numerical representations of the hour and the minutes:

var words = new Array();  
var now = new Date();  
var hour = now.getHours();  
var mins = now.getMinutes();  

The following is a little bit of a hacky approach but it avoids complicated if statements. Each case tests which five minute bracket the current minutes falls in and adds the relevant words to our array:

  switch(true){
    case (mins >= 55): words = [five, to]; hour++; break;
    case (mins >= 50): words = [ten, to]; hour++; break;
    case (mins >= 45): words = [quarter, to]; hour++; break;
    case (mins >= 40): words = [twenty, to]; hour++; break;
    case (mins >= 35): words = [twenty, five, to]; hour++; break;
    case (mins >= 30): words = [half, past]; break;
    case (mins >= 25): words = [twenty, five, past]; break;
    case (mins >= 20): words = [twenty, past]; break;
    case (mins >= 15): words = [a, quarter, past]; break;
    case (mins >= 10): words = [ten, past]; break;
    case (mins >= 5):  words = [five, past]; break;
    case (mins < 5):   words = [oclock]; break;
  }

Because Javascript's Date object returns hours in twenty-four hour format we have to do a little maths to change things to twelve hour format.

if(hour > 12){  
  hour = hour - 12;
} else if(hour < 1){ 
  hour = 12; 
}

Now it's just a case of pushing the words 'it is' and the current hour to our words array. Looping through each word then splitting each word by the comma to get each cell ID. With this we can set the class to 'alight'.

words.push(itis, hourwords[hour]); 

words.forEach((word) => {  
  word.split(',').forEach((letter) => {
 document.getElementById(letter).classList.add("alight");
  });
});

To get this all running we fire the showClock() function. To update it we run a function every five seconds to see if the current minutes is a multiple of five and if so, run the showClock() function again.

(function() {
  showClock();
  setInterval(() => {
    if(new Date().getMinutes() % 5 == 0){ 
      showClock();
    }
  }, 5000);
})();

To make the letters glow apply a text-shadow with a light colour against a dark background:

.alight { 
  text-shadow: 0px 0px 12px #ccc; 
  color:       #fff; 
}

CSS Selectors

I can never remember the syntax for this stuff.

Select by attribute presence

<p>This paragraph has a <a href='#'>link</a> in it.</p>  
[href] { background : cyan; }

This paragraph has a link in it.


Combine with element

<p title='wigwam'>This paragraph has a title attribute.</p>  
p[title] { background : cyan; }  

This paragraph has a title attribute.

Putting a space between p and [title] would specify the children of paragraph with title attributes.

<p>This paragraph has a couple of <span title='wigwam'>spans</span> with a <span title='wigwam'>title</span> attributes.</p>  
p [title] { background : cyan; }  

This paragraph has a couple of spans with a title attributes.


Target an attribute and its contents

The content of an attribute can also be specified by having the attribute equal something

<p>This paragraph has a couple of <span title='t1'>spans</span> with different <span  
title='t2'>title</span> attributes.</p>  
[title="t1"] { background : cyan; }
[title=t2] { background : yellow; }

This paragraph has a couple of spans with different title attributes.


Target a specific word in the attribute

Using the tilde (~) it is possible to target by a single string in a space separated list.

<p>This paragraph has a couple of <span title='one two three'>spans</span> with different <span title='one three four'>title</span> attributes.</p>  
[title~="one"] { font-weight : bold; }<br/>
[title~="two"] { background : cyan; }

This paragraph has a couple of spans with different title attributes.

It is also possible to target a substring at the end using the dollar ($) sign.

<p>This paragraph has a couple of <span title='twotone'>spans</span> with different <span title='donedown'>title</span> attributes.</p>  
span[title$="one"] { background : cyan; }  

This paragraph has a couple of spans with different title attributes.

To target a substring at the beginning use the caret (^) sign.

<p>This paragraph has a couple of <span title='twotone'>spans</span> with different <span title='onedown'>title</span> attributes.</p>  
span[title^="one"] { background : cyan; }  

This paragraph has a couple of spans with different title attributes.

It is also possible to target an exact match followed by anything separated with a dash using the pipe (|) symbol.

<p>This paragraph has a couple of <span title='one-up'>spans</span> with different <span title='one-down'>title</span> attributes.</p>  
span[title|="one"] { background : cyan; }  

This paragraph has a couple of spans with different title attributes.

Lastly we can do a full text search using the asterisk (*)

<p>This paragraph has a couple of <span title='donedown'>spans</span> with different <span title='doneup'>title</span> attributes.</p>  
span[title*="one"] { background : cyan; }  

This paragraph has a couple of spans with different title attributes.


Attribute stacking

Attribute selection can also be stacked to give even finer control of selection.

<p>This paragraph has a couple of <span class='onetwothree' title='onetwothree'>spans</span> with different <span class='twothreefour' title='onetwothree'>title</span> attributes.</p>  
span[class~='one'][title*="two"] { background : cyan; }  

This paragraph has a couple of spans with different title attributes.


Outputting attributes

An element's attribute can also be used in pseudo-elements for output.

<p>This paragraph has a <a href='https://lewiswalsh.com'>link</a> with a dynamically printed href.</p>  
a:after { content: " (" attr(href) ")"; }  

This paragraph has a link with a dynamically printed href.


Case sensitivity

By default all CSS attribute selectors are case-sensitive. To make the selection case-insensitive add an 'i' to the end of the selection.

[title*="one" i] { }

GeoIP blocking and redirecting on Apache

Recently I was asked to configure a WordPress install to block any traffic from Russia, and redirect any traffic from the USA to another US-centric site.

First thing to do is install mod-geoip for Apache (in my case on Ubuntu):

sudo apt install libapache2-mod-geoip  

Next uncomment the GeoIP database in the GeoIP conf file (/etc/apache2/mods-available/geoip.conf):

<IfModule mod_geoip.c>  
  GeoIPEnable On
  GeoIPDBFile /usr/share/GeoIP/GeoIP.dat
</IfModule>  

Restart Apache:

sudo systemctl restart apache2  

With GeoIP enabled we can apply our block and redirect in .htaccess

GeoIPEnable On

# deny for Russia
SetEnvIf GEOIP_COUNTRY_CODE RU DenyCountry

# redirect for USA
RewriteEngine on  
RewriteCond %{ENV:GEOIP_COUNTRY_CODE} ^(US)$  
RewriteRule ^(.*)$ https://usa-site.com/$1 [L]  

A list of ISO Alpha-2 country codes can be found on WikiPedia, and many other places online.

Javascript Media Queries

Javascript media queries used to be a pain in the butt. I won't go in to why because thanks to window.matchMedia it's now very easy.

window.matchmedia has a matches property which returns a boolean value, so to run it once just use it something like and if statement:

if(window.matchMedia("(min-width: 500px)").matches) {  
  /* the viewport is at least 500 pixels wide */
} else {
  /* the viewport is less than 500 pixels wide */
}

It's also possible to add a listener to call a function to run some code whenever the query return value changes:

if(matchMedia){ // Only run if matchMedia is supported  
  const media_query = window.matchMedia("(max-width : 500px)"); // Create media query
  media_query.addListener(widthChange); // Optional listener with function to run
  widthChange(media_query); // Initial run
}

function widthChange(media_query){  
  if(media_query.matches){ // If the media query resolves true
    // window width is under 500px wide
    console.log('Under');
  } else {
    // window width is more than 500px wide
    console.log('Over');
  }
}

Neat Vanilla JS on HackerNews

I recently saw this video by Gordon Zhu highlighting the small but extraodinarily clever JavaScript on the Hacker News website.

I felt it would help me if I simplified it and broke down the steps for future reference. I'm using Paul Buchheit's code verbatim, I hope he doesn't mind, full credit to him.

What does this achieve?

This code is called when an anchor (a link) is clicked to vote up an item on Hacker News. It casts the vote and hides the vote up link so it can't be clicked again.

HTML

The markup for this is REALLY simple. It's a perfect example of progressive enhancement. The full URL is present in the HREF so if JavaScript is turned off the onclick is never called but the vote is still cast.

<a id="up_11659026" href="vote?for=11659026&dir=up" onclick="return vote(this);">Vote Up</a>  

JavaScript

No JQuery here! This is concise and elegant vanilla JS. The only JS on the site is following two functions, with the latter calling the former.

function hide(id){  
  var el = document.getElementById(id);
  if (el) { el.style.visibility = 'hidden'; }
}
function vote(node){  
  var v = node.id.split(/_/);
  var item = v[1];
  hide('up_'   + item);
  hide('down_' + item);
  var ping = new Image();
  ping.src = node.href;
  return false;
}

How does it work

  1. When the link is clicked the vote() function is called with the anchor (<a>) element passed as this
  2. The id, up_11659026, is split at the underscore and the parts passed to an array, v
  3. A new var, item, is given the value at v[1], in this case 11659026
  4. Two calls are made to the hide() function, one with the value up_11659026, and one with the value down_11659026
  5. The hide() function looks for an element with the passed id, if it finds it, the visibility of the element is set to hidden
  6. A var is created, ping, and is an instance of JavaScript's Image object.
  7. By setting the src of ping to the href of the clicked element, the URL is called (in this case a vote is cast by the back-end)
  8. Nothing is returned or rendered as it's unnecessary.
  9. We return false to stop the URL being followed by the browser
  10. If JavaScript is turned off in the browser, the URL is followed when clicked and the same outcome occurs.