2. Introduction to JavaScript


A quick note about the practicals...

Remember to use Mozilla Firefox to do these practicals. Please do NOT use Microsoft Internet Explorer / Edge, this is not suitable for web development. Coding should be done in Notepad++

You do not have to run every bit of code in this document. Read through it, and have a go where you feel it would help your understanding. If I explicitly want you to do something, I will write an instruction that looks like this:

This is an instruction that tells you something to either think about, or do.

Shortcuts: Part 1 Part 2


In which we get stuck in at the deep end with some JavaScript, handle some events and transform some coordinates!

Part 1

JavaScript: In at the deep end

JavaScript is a programming language that, when applied to an HTML document, can provide dynamic interactivity on websites. We already know that JavaScript is held in <script> tags in the HTML, and there are two sets of these in the code we used last time to make our Leaflet Map. Here they again:

1:

<script>
	// this is a variable that holds the coordinates for our map and marker
	var map = L.map('map');
	
	// this is a variable that holds the values for the coordinates [latitude, longitude] of the centre of the map
	var mapCentre = [53.466502, -2.235387];
	
	// set the map to use the above coordinates, and to zoom level 16
	map.setView(mapCentre, 16);
	
	// this is a variable that holds a reference the tiles that will be used for the basemap
	var tiles = L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {attribution: '&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'});
	
	//add the tiles to the map
	tiles.addTo(map);
</script>

2:

 <script src="https://unpkg.com/leaflet@1.4.0/dist/leaflet.js"></script>

The first important thing to notice is that these two tags use very different approaches to loading JavaScript onto a web page. The first one simply encloses some JavaScript code, but the second one loads some external JavaScript from elsewhere on the web using the src= attribute of the <script> tag to describe the URL of the JavaScript file (in this case, a website called unpkg.com).

So what exactly is that second code snippet doing? It is downloading and then executing some JavaScript code in order to provide you with all of the functionality required to build a Leaflet Map. We put statements like this in the head of the web page so that they load into the browser before the content (in the body of the page), meaning that all of the functionality from the Leaflet library is ready when we need it in the body.

Once the required library is downloaded, we can then write our own code (as in snippet 1). This is the part that we will be focussing upon today.

JavaScript: The Basics

Before we can do any meaningful programming, there are a few interconnected basics that you need to know. Read over these just so you have the gist, then move on. Don’t worry if it doesn’t quite make sense right away, once you start writing some code it will all become clear - I promise!

Comments:

As we discussed last week, it is normal when writing code to include comments that explain exactly what you are doing and why. This is important as it makes your code readable to others (which is especially important for marking purposes!) as well as providing a valuable aide memoire to yourself - you’ll be amazed how alien your own code can seem when you haven’t looked at it for a while!

Comments in javaScript are just like the ones that you learned about in HTML last week. There are three ways to do a comment in JavaScript:

Doc comment (should be used at the top of each function):

 /**
  * Everything here is a comment.
  */

Multiline comment (should be used for sections of code):

/*
 Everything in here is a comment.
*/

/* Also works like this */

Single line comment (should be used for every individual line):

// This is a comment

The top two are essentially the same - the only difference is that doc comments will appear in automatically generated docs (documentation), and multiline comments won’t. Docs are like instructions that are generated automatically by software, but you don’t need to worry about them just yet…

To go back to our bit of code from earlier then, we can see that we have single line comments in there already. Comments might start to seem repetitive at first, but as your scripts expand they will be come a very valuable part of the code. As a rule of thumb, well-written code should be roughly one-third code, one-third comments, and one third whitespace. Programmers call this the Rule of Thirds. Whilst this doesn’t affect the functionality of the code, it makes it much easier to read, and I promise it will save you a lot of time later!

The JavaScript Console

Another thing that we need to get familiar with before we start programming is JavaScript Console, this is the most important tool available to a programmer when writing an interactive website.

When programming, it is often handy to be able to see what your code is doing, or to make your code print out values that it knows for testing purposes. For those times, there is the JavaScript Console, which is built into most browsers (but the Firefox one is best). To open the console in Firefox, press Alt to make the menu appear at the top of the screen, and go to Tools > Web Developer > Web Console.

image

Alternatively, you can simply press the menu button and go to Developer > Web Console.

image

image

image

Getting your code to write to the console is as easy as using console.log(). For example:

console.log("Hello World!");

Paste the above in between the <script> tags of a fresh web page, open the page then take a look in the console. Did it work?

Another common reason to use the console, is debugging, which is essentially trying to find mistakes (or bugs) in your code. For example, imagine that you had some code that did some basic maths:

var a = b + c;

Now imagine that it wasn’t working. How would you figure out the problem? The easiest way to solve this would be to have a look in each of the variables. Here is an example that includes comments to explain the debugging process:

//check the input variables:
console.log(b);		//prints 2
console.log(c);		//prints "apple"

//"apple" isn't a number, so you cannot use it in an equation...
//change the value of d to a number, and it should work fine!

That example might seem silly, but this sort of thing happens a in programming (which is why it is important to know the difference between a String and a Number…). To print out data or information whilst you’re programming, you simply need to add console.log() statements to your code, and watch the statements appear whilst the script runs. This will make your life so much easier!

The console will also show you Error Messages if anything goes wrong with your HTML, CSS or JavaScript. Error Messages will tell you what is wrong and even which line of your code has the problem, so the JavaScript console is always the first place thet you should look if something isn’t working - it will often tell you the problem straight away!

Variables:

Variables are containers that you can store values in - they are used in all programming languages. You start by declaring a variable with the var keyword (short for “variable”), followed by any name you want to call it. So, for example, if I wanted a variable that held my name, I might call it myVariable like this:

var myVariable;

I can then store a value in it using the = operator, like this (we look at operators in a second…):

myVariable = "Jonny";

or, for efficiency, do both at once:

var myVariable = "Jonny";

Note: every JavaScript statement has to end with a ;, if you forget this, your script probably won’t work, or you may get some unexpected results!

Lets’ test if that worked…

Paste the below code snippet into your web page, open it in Firefox then take a look at the console. Does it do what you expected?

var myVariable = "Jonny";

console.log("My name is " + myVariable);

Variable names can’t have any spaces or characters that are not a-z, A-Z or 0-9 in them. By convention, variables in JavaScript are normally written in camel case (as opposed to upper case or lower case), which is demonstrated above. In camel case, the first word should start with a lower case letters, and all subsequent words should start with an upper case letter (making sure that there are no spaces!). Here are some examples:

  • camelCase
  • aReallyLongVariableName
  • short
  • jonnyHuck

You can change the value in a variable at any time, simply by setting it again using the = operator:

myVariable = "Patrick";

Did you notice that you didn’t need to declare the variable with the var keyword this time? It’s because you’ve already done it the first time around, so the variable already exists!

Try to change the value of myVariable to Patrick in your web page. Remember that it needs to be after the variable declaration, but before the console.log() statement!

You can store any value in a variable, and most of the time you do not need to worry about what ‘type’ they are. For programmers, however, there are several different types of values, and it is important that you know the differences between them for when we start doing more advanced coding in the next few weeks. Here are the main types:

Variable Explanation Example
String A string of text. To signify that the variable is a string, you should enclose it in quote marks. var myVariable = "Bob";
Number A number. Numbers don’t have quotes around them. var myVariable = 10;
Boolean A True/False value. The words true and false are special keywords in JavaScript, and don’t need quotes. var myVariable = true;

A variable that does not contain a value of one of these types is said to be null.

Operators

An operator is a mathematical symbol that produces a result based upon two values (which are normally stored in variables). In the following table you can see some of the simplest operators, along with some examples:

Operator Explanation Symbol(s) Example
add/concatenate Used to add two numbers together, or concatenate two strings together. + 6 + 9;

"Hello " + "world!";
subtract, multiply, divide These do what you’d expect them to do in basic maths. -, *, / 9 - 3;

8 * 2;

9 / 3;
assignment operator You’ve seen this already, it assigns a value to a variable. = myVariable = "Bob";

There are more operators, of course, but we can look at them later in the course…

Functions

Functions are a way of packaging code that you want to reuse, so that whenever you want the functionality of that code you can call the function with it’s name, rather than constantly rewriting the entire script. You have already seen one function in this practical: console.log() is a JavaScript function that writes something to the JavaScript Console.

Some functions (e.g. console.log()) already exist in the JavaScript, or are brought in with libraries that you load (Leaflet provides lots of functions for you to use). Others, however, are created by you, and in fact, most of your code will be written in the form of functions. Fortunately, creating a function is not very hard, here is one now:

 /**
  * Return my name
  */
 function whatsMyName(){
 	
 	//store my name in a variable as a String
 	var myName = "Jonny";
 	
 	//return that variable
 	return myName;
 }

Note how the code within the function is indented, this helps to make the code readable, and is an important for well-written code. A portion of the marks for the assessments in this course are for code presentation, including sensible variable and function naming, propoer indenting, commenting, and the rule of thirds.

Here are the components of a function:

  1. The keyword function tells JavaScript to make a function
  2. The function needs a unique name (again in camelCase)
  3. Any arguments for the function go inside some parentheses (( )). I will talk about these in a minute…
  4. The contents of the function go between the curly braces ({ }) - the contents are normally indented to make them easier to read.
  5. The optional return keyword tells the function to output (return…) the result (myName in this case). Some functions just do something (e.g. console.log()) and don’t actually need to return a value, but if you do need to return a result then return is how to do it!

Can you see all of those parts in the above example?

It is important to understand that writing a function like is shown above creates the function, i.e. it means that JavaScript knows what to do when you tell it to run. The contents of the function do not actually do anything until you tell the browser to use that function - this is called calling the function, and you do this simply by writing the name of the function, followed by the parentheses:

var result = whatsMyName();

In this case, the function whatsMyName() will run, and then whatever value is passed in the return statement will be stored in the variable result.

Try to replace your code that prints out my name with the following version that uses a function

 /**
  * Return my name
  */
 function whatsMyName(){
 	
 	//store my name in a variable as a String
 	var myName = "Jonny";
 	
 	//return that variable
 	return myName;
 }

//get my name from the function and store it in a variable called result
var result = whatsMyName();

//write the contents of the result variable to the console
console.log(result);

Arguments for functions

As useful as the above function is, most functions require values to be given to them, with which they can do things. Values are passed to functions in the form of arguments, which are enclosed within the parentheses ( ( ) ) of the function and are separated by commas if there are more than one:

/**
 * Add two numbers together
 */
function addUp(arg1, arg2) {
  return arg1 + arg2;
}

In this case, you could type:

console.log( addUp(2,2) );

And you would print out 4!

How would you write a function to multiply two numbers together? Either test your solution or check with Jonny…

Similarly, consider:

/**
 * Returns the square of a number
 */
function square(number) {
  return number * number;
}

In this case, you could call it using

var result = square(6);

Add the above square function and call to your web page and pass the result to console.log(). Is the answer what you expected?

In this example, the function square contains a single statement that says to return the argument of the function (that is, number) multiplied by itself.

Take a second to remember and understand the difference between defining and calling a function. Defining the function simply creates it, it won’t actually do anything until it is called.

So there you have it: comments, the JavaScript Console, variables, operators and functions: all you need to get stuck into some serious JavaScript!

I know that seems a lot to take in, but I don’t expect you to memorise it all at once! remember that you can always just refer back to this website whenever you need it. Right, I think it’s time to have a go…


Part 2

JavaScript Events

The most interesting thing about JavaScript is that it is event driven, meaning that JavaScript responds to interactions from the User with the Web page, running different blocks of code depending upon what they do. If you have ever clicked a button on a website and something has happened in response, that is because JavaScript noticed you do it, and responsed accordingly!

Consider this button, and then click on it…

That worked because Buttons in HTML know to generate an event called onclick, which is fired every time someone clicks on it. I have the following JavaScript listening for that event, meaning that as soon as someone clicks on the button, the onclick event is fired and the function that I have set as the listener is called. Here is the code that is fired by the onclick event:

alert('You clicked my button!');

alert is a function that generates a small box containing a message that you pass to it as an argument. I have attached it to the onclick event of that button simply by setting the value of the onclick attribute of the <button> tag to alert('You clicked my button!');. Here is the whole line of code:

<button onclick="alert('You clicked my button!');">Go on, give me a click...</button>

Read the above code snippet carefully to make sure that you understand how it works (you don’t need to implement it, just understand the principle).

Getting control of our code

Now that we know all about events, we are going to use them to change the way that we create our map. We are firstly going to wrap our code in a function called initMap() (short for initialise map) and then we are going to use an event to call that function. Let’s get cracking…

Make a fresh web page again using this template, call it week2.html, then swap out the JavaScript within the <script> tags for this alternative:

//setup global variables
var map, marker, transformer;
	
/**
 * Initialise the Map
 */
function initMap(){
	
	// this is a variable that holds the reference to the Leaflet map object
	map = L.map('map').setView([55, -4], 6);   //this will give a map of Great Britain

	// this adds the basemap tiles to the map
	L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
		attribution: '&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
	}).addTo(map);	
}

Do you see what is happening here? We are declaring the map variable in the global scope so that it can be reached from within functions, and then we have ewrapped the rest of the code in a function called initMap().

If you ran the code now, it would not work. This is because you have defined initMap(), but it never gets called! To call it, we are going to add the function to an event listener, listening for the onload event of the <body> element. This means that the function will be created as the page loads, but will not actually run until after the page is loaded (as the onload event is not called until the <body> has loaded completely). We do this just like I did with the button above - by setting the onload attribute of the body tag:

Replace the old <body> opening tag with this new one to make initMap() the listener for the onload event:

<body onload='initMap();'> 

Now if you refresh the page in your browser it should work like a charm!

Congratulations, you just used your first event!

Leaflet Events

Now consider a simple Leaflet map like this:

With all of that functionality (zooming, panning, clicking etc.), there is the potential for interacting with a great many events (here is a list of all of the options) and that is exactly what we are going to do now!

One of the first big programming successes that I had in my first job (an engineer for a Wind Farm company) was making a version of the (still pretty new) Google Maps that could operate using Ordnance Survey British National Grid (BNG) Coordinates - this is a type of Mercator projection, which is used in most British maps and datasets. This was a really valuable tool for our company, as it meant that Google Maps was interoperable with all of our existing GIS systems and datasets.

Today we are going to do something similar - make a web map that you can click on to receive the location in BNG coordinates.

To do whis we have four simple steps:

  1. Make a Leaflet Map (already done)
  2. Make a click listener to record when and where a user clicked on the map
  3. Transform the coordinates of the click to British National Grid
  4. Add a popup and a marker to the map displaying the coordinates

The result will be something like this:

Not bad eh? Let’s give it a bash…

Making the click listener

One of the best things about Leaflet is that it makes things really easy to do in comparison with some of the other map libraries that are available for JavaScript. To add a click listener, simply add this single line as the last thing inside the initMap() function:

// add listener for click event on the map
map.on('click', onMapClick);

This is a function built into the map object, which takes two arguments:

  • The name of the event to listen to (click)
  • The function to call when the event is fired (onMapClick)

In plain English it simply says that when someone clicks on the map, call the function onMapClick().

Simple, right?

The next step is obviously to define what onMapClick() should do, so lets do that:

Copy and paste the below function into the <script> tags, ensuring that it is outside the initMap() function!

/**
 * Event handler for map click
 */
function onMapClick(e) {
	
	if (marker){
		map.removeLayer(marker);
	}
	
	marker = L.marker(e.latlng);
	
	marker.addTo(map);
	
	var popup = marker.bindPopup( e.latlng.toString() );
	
	popup.openPopup();
}

That should give you something that looks like the map below. If you click on it, it should return the coordinates of the pont that you clicked!

That is some pretty quick progress! one line of code to add an event listener and a small function to call when the event is fired and we have a useful map - this can give us the coordinates of anywhere in the world, just by clicking on it!

Find the coordinates of somewhere you know, just to prove it works!

Okay, as good as that it, it’s not much use if we don’t understand exactly how it works… Lets break down that a function a little bit. There are four main parts to this function, and we will look at each one individually:

The first part is, of course, the function declaration:

function onMapClick(e) {

Notice that is takes an argument, e. When you define an event listener, if you include an argument, then it will pass you an object containing information about the event. In this case, it is a special object defined by Leaflet called a MouseEvent, which contains information about the event such as the time at which it happened and the geographical coordinates at which the click occurred on the map.

You could, of course, call the argument anything that you like, because the function creates a variable for the incoming argument to use according to the name used in the definition. I simply use e (for event) as it is simple!

Next up, we have the following statement:

if (marker){
	map.removeLayer(marker);
}

This is an example of a conditional statement, that is, a block of code that only runs in certain circumstances. This one checks if a marker already exists on the map, and if so it removes it before adding the new one. We are going to study conditional statements in more detail next week, so for now it’s fine to just accept that this is what it does.

Now we have cleared the map, it is time to make a new marker at the location of the click:

marker = L.marker(e.latlng);
marker.addTo(map);

You have seen something like this already last week. L is the Leaflet object, containing all of the map functionality (e.g. L.map() where you create the map and L.tileLayer() where you add the OpenStreetMap tiles). In this case, we are using L.marker() which is a function that returns a marker object located at the coordinates specified in the MouseEvent, and storing it in the global variable marker. The next line then adds this marker to the map (specified using the map global variable).

Finally, we add the popup that specifies the actual coordinates:

var popup = marker.bindPopup( e.latlng.toString() );
popup.openPopup();

This is quite similar to the above pair of statements, except this time, popup is declared as a local variable, which only exists inside the function. This is because we never use the popup variable after the function has run, so we have no need to make it global. (marker), on the other hand, is global because it needs to still exist next time a click event is fired so that it can be removed from the map.

marker.bindPopup() is a function that adds the popup to the marker, and specifies what the contents will say, returning the popup object to be stored in the popup variable. The content of the popup is defined as e.latlng.toString(), which is a chain of two javaScript Statements: e.latlng extracts the location (a Leaflet LatLng object); and then .toString() converts it from an object to a String, so that it can be displayed.

popup.openPopup() then simply opens the popup so that the user can see it!

Comment each individual line of the function to make sure that you understand how it works

Coordinate Transformations

The last job for the day is simply to transform those coordinates from WGS84 Longitude-Latitude pairs to Ordnance Survey National Grid. Back in the 2000’s, I had to write out hundreds of lines of code based upon books of coordinate transformations like this (check pout pages 48-65) and this (check out appendix C), and web resources like this (This site, along with this one is still one of the most valuable sites on the web for a GIS professional).

Fortunately, however, there are now libraries preventing us needing to write lots of code to be able to undertake coordinate transformations - and we will be using one of them: Proj4js. Lets get it imported onto our page so that we can get started right away:

Add this line to your <head>:

<!-- Load the Proj4js library -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/proj4js/2.5.0/proj4.js"></script>

Just like Leaflet gives you all of the tools that you need to make a map, proj4 gives you all of the tools that you need to be able to transform coordinates! Lets give it a go. proj4js is a simple library, you create a proj4 object using two coordinate reference systems, and then use that object in order to do your transformations. Here we go then, firstly, let’s create our object:

Add the following snippet to initMap() before setting the click listener

var p_wgs84 = "+proj=longlat +datum=WGS84 +no_defs";
	
var p_osgb = "+proj=tmerc +lat_0=49 +lon_0=-2 +k=0.9996012717 +x_0=400000 +y_0=-100000 +ellps=airy +towgs84=446.448,-125.157,542.06,0.15,0.247,0.842,-20.489 +units=m +no_defs";
	
transformer = proj4(p_wgs84, p_osgb);

Let’s have a look at that code snippet in more detail. There are three steps there:

Comment the new code to demonstrate that you understand it

Now we have our object, it’s time to use it! proj4 objects contain two functions:

.forward() transform between the first coordinate reference system and the second (wgs84 to osgb in our case)
.inverse() transform between the second coordinate reference system and the first (osgb to wgs84 in our case)

Our goal is simply to transform the coordinates forward() (they start in WGS84 and we want them to be OSGB) inside the onMapClick() event listener, just in time to display them on the popup. As we have already set up our transformer variable, this can be achieved with a single line of code:

// transform coordinates to OSGB
var xy = transformer.forward([e.latlng.lng, e.latlng.lat]);

Copy and paste this line of code into the onMapClick() function

This might look a little complex, but all that it is doing is:

  • declaring a variable called xy to store the result
  • extracting the longitude and latitude values from the e.latlng object using .lng and .lat respectively
  • passing the resulting coordinates as an argument to the forward() function, so that it can transform them and return the result

Finally, we simply need to update the popup statement to reflect the new data:

// create a popup displaying OSGB coordinates
var popup = marker.bindPopup(parseInt(xy[0]).toString() + ", " + parseInt(xy[1]).toString());

Update your popup statement to reflect the changes above

NOTE: You will notice that there are some unfamiliar bits of code using square brackets [ ] in this sectipn pof the practical - we will be looking at these later in the course, so feel fre to ignore them for now - it is suffient to accept that this is the format in which proj4js expects its input data to be.

If all has gone to plan, then you should have something like this:

If so, then congratulations! You dove into the deep end of JavaScript and came up swimming!

One last thing…

If you want a copy of the the Mercator distortion example I gave in the lecture (which is based upon the Google Maps JavaScript API rather than Leaflet), you can get it here or on the solutions page.

Finished!

Some of the material on these pages is derived from the excellent Leaflet Tutorials and Mozilla Developers websites. Mozilla Developers by Mozilla Contributors is licensed under CC-BY-SA 2.5.