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 are again:
1:
<script src="https://unpkg.com/leaflet@1.5.1/dist/leaflet.js"></script>
2:
<script>
// this is a variable that holds the coordinates for our map and marker
const map = L.map('map');
// this is a variable that holds the values for the coordinates [latitude, longitude] of the centre of the map
const 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
const tiles = L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {attribution: '© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'});
//add the tiles to the map
tiles.addTo(map);
</script>
NOTE: I Haven’t asked you to do anything with these bits of code, they are simply there for you to read - remember that you only need to do something like that if I ask you using an instruction that looks…
…like this!
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 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, we are downloading it from a website called unpkg.com
). The second one, on the otehr hand, contains some JavaScript code itself.
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. BUT for this to work we need to think carefully about how the browser interprets a web page - it does it just like a human would, from top to bottom. It is therefore vitally important that we have imported the Leaflet library before we try to use any of its functionality in our own code! All of our code must therefore be below the <script>
tags where we import the library.
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:
Documentation comment (should be used at the top of each function to describe what a function does and how it works):
/**
* Everything here is a comment.
*/
Multiline comment (should be used for sections of code to describe what they do):
/*
Everything in here is a comment.
*/
/* Also works like this */
Single line comment (should be used for every individual line to explain what it does):
// 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). You can make your code write things to the console in order to test if your code is working, and the console will also report error messages to you in the event that there is a problem with your code. These error messages are really useful, and will help you to solve problems with your code very quickly.
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.
Alternatively, you can simply press the menu button and go to Developer > Web Console.
Getting your code to write to the console is as easy as using console.log()
. For example:
console.log("Hello World!");
Follow this link to get a template for a blank web page containing a map, and copy it into a new file called
week2.html
. Then paste the above line in between the<script>
tags, open the page then take a look in the console. Did it work?
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, and even which line of your code is causing it!
Variables:
Variables are containers in which you can store values - they are used in all programming languages. You start by declaring a variable, which you do with one of three keywords: const
, let
or var
; followed by any name you want to call it.
To keep things simple, we will only use const
and let
in this course - and here is when you would use them:
const
(constant) should be used if you are not planning to change the value in the variable after the initial definitionlet
should be used if you are planning to change the value in the variable after the initial definition
So, for example, if I wanted to create a variable that held my name, I might call it myName
like this:
let myName;
I can then store a value in it using the =
operator, like this (we look at operators in a second…):
myName = "Jonny";
or, for efficiency, do both at once:
let myName = "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!
Let’s 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?
// create a variable that stores your name (you will need to change it if you are not also called Jonny...)
let myName = "Jonny";
// print the stored name to the console
console.log("My name is " + myName);
Variable names can’t have any spaces or characters that are not a-z
, A-Z
or 0-9
in them. They cannot begin with a number (though they can contain them) and they can’t have any spaces 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
short2
jonnyHuck
Conversely, here are some invalid variable names:
1
2bigFish
myLovely variable
You can change the value in a variable that was set using let
at any time, simply by setting it again using the =
operator:
myName = "Jonathan";
Did you notice that you don’t declare the variable with the let
keyword this time? It’s because you’ve already done it the first time around, so the variable already exists! If you try and re-declare it, you will get an error. Also remember that if you declare it with const
then you cannot change the value and you will get an error if you try - this is important as it prevents you from accidentally overwriting variables when your code gets longer and more complex!
Try to change the value of
myName
in your web page. Remember that it needs to be after the variable declaration, but before theconsole.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. | let myVariable = "Bob"; |
Number | A number. Numbers don’t have quotes around them. | let myVariable = 10; |
Boolean | A True/False value. The words true and false are special keywords in JavaScript, and don’t need quotes. |
let myVariable = true; |
null |
null means ‘nothing’, and is used to tell a variable that it contains nothing |
let myVariable = null; |
A variable that has been created but does not have a value assigned to it is said to be undefined
.
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 will 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, for example, provides lots of functions for you to use). Others 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:
/**
* A function to return my name
*/
function whatsMyName(){
//store my name in a variable as a String
const 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:
- The keyword
function
tells JavaScript to make a function - The function needs a unique name (again in camelCase)
- Any arguments for the function go inside some parentheses (
( )
). I will talk about these in a minute… - The contents of the function go between the curly braces (
{ }
) - the contents are indented to make them easier to read. - 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 thenreturn
is how to do it!
Make sure that you can identify all of those parts in the above example. If not - ask Jonny or one of the GTAs for help
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 function does not actually do anything until you tell the browser to use it - this is called calling the function, and you do this simply by writing the name of the function, followed by the parentheses:
const result = whatsMyName();
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.
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
.
In your
week2.html
file, try to replace the code we added that prints your name with the following version that uses a function instead:
/**
* Return my name
*/
function whatsMyName(){
//store my name in a variable as a String
const myName = "Jonny";
//return that variable
return myName;
}
//get my name from the function and store it in a variable called result
const result = whatsMyName();
//write the contents of the result variable to the console
console.log(result);
Make sure that you understand how that works, if not, ask!
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
!
Write a function to multiply two numbers together and add it to your code so that the solution is printed to the console
Phew! 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.
This is the end of Part 1 so stop here as there is more lecture material to go through before we move on to Part 2…
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!');
Notice the use of the String 'You clicked my button!'
.
How can you tell that
'You clicked my button!'
is a String? If you aren’t sure, ask Jonny or one of the GTAs.
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). You should be able to identify the event (
onclick
) and the listener (alert('You clicked my button!');
)
Getting control of our code
Now that we understand 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
week2b.html
, then delete the code in the<script>
element and replace it with this:
//setup global variables
let map, marker, transformer; // use let because these will be edited later
/**
* Initialise the Map
*/
function initMap(){
// set the centre of the map as a variable
const mapCentre = L.latLng(55, -4);
// this is a variable that holds the reference to the Leaflet map object
map = L.map('map').setView(mapCentre, 6);
// this adds the basemap tiles to the map
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
attribution: '© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
}).addTo(map);
}
Do you see what is happening here? We haveput the code to set up the map into 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.
If the
onclick
event of abutton
is fired when the button is clicked, when do you think theonload
event of thebody
is fired? Check if you are right with Jonny or one of the GTAs
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 called when the <body>
has finished loading). 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 makeinitMap()
the listener for theonload
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 out of University (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 (including all of its super valuable satellite imagery) 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 this we have four simple steps:
- Make a Leaflet Map (already done!)
- Make a
click
listener to record when and where a user clicked on the map - Transform the coordinates of the click to British National Grid
- Add a
popup
and amarker
to the map displaying the coordinates (we did this last week too…)
The result will be something like this:
Not bad eh? Let’s give it a bash…
Adding a Popup
The first thing that we will do is create a function to add the marker and the popup to the map:
Make an empty function called
addMarker
that takes in a single argument calledlatLng
- make sure that it is outside theinitMap()
function!
Firstly, let’s add a marker to the map (just like last week). The latlng
is a special type of variable defined by Leaflet to hold coordinate pairs - we will talk more about this in a future week…
Add the following snippet to your
addMarker
function
// create a marker at the clicked location
marker = L.marker(latLng);
// add the marker to the map
marker.addTo(map);
Then, let’s add the popup (again, just like last week)
Add the following snippet to your
addMarker
function
// create a popup displaying OSGB coordinates
const popup = marker.bindPopup(latLng.toString());
// open the popup
popup.openPopup();
Note how we use latLng.toString()
converts latLng
from a special type to a String, so that it can be displayed
Finally, let’s test it…
Add the following function call to the end of your
initMap
function, this should add a marker and popup at the centre of the map. Refresh your page in the browser - did it work?
// add a marker at the centre of the map
addMarker(mapCentre);
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 theinitMap()
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 as a String (
'click'
) - The function to call when the
click
event is fired (onMapClick
)
In plain English it simply says that when someone click
s on the map
, the browser should call the function onMapClick()
.
Simple, right?
The next step is obviously to define what onMapClick()
should do, so lets do that:
Make an empty function called
onMapClick
that takes in a single argument callede
- make sure that it is outside theinitMap()
function!
Now we have an empty function set as the click listener, we simply need to extract the coordinates of the click and pass them to the addMarker
function. While we’re at it, let’s remove the previous marker to kep things neat:
Add the below snippet to your
onMapClick
function:
// remove the previous marker from the map
map.removeLayer(marker);
//add the new marker
addMarker(e.latlng);
That should give you something that looks like the map below. If you click on it, it should return the coordinates of the point 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!
Click on your map to find the coordinates of somewhere you know, just to prove it works!
Okay, as good as that is, it’s not much use if we don’t understand exactly how it works… Lets break down that a function a little bit.
The key to it is the argument, e
. When you define an event listener, if you include an argument, then it will pass you a special variable containing information about the event. In this case, it is a special type of variable 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. e.latlng
extracts the location.
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!
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()
immediately **before** the call to `addMarker()`
const wgs84 = "+proj=longlat +datum=WGS84 +no_defs";
const 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(wgs84, osgb);
Let’s have a look at that code snippet in more detail. There are three steps there:
- create a variable called
wgs84
containing the proj4 string for the wgs84 coordinate reference system - create a variable called
osgb
containing the proj4 string for the British National Grid coordinate reference system - use the
proj4()
function to create aproj4
object capable of transforming between the two specified coordinate reference systems
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 addMarker()
function, 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
const xy = transformer.forward([latLng.lng, latLng.lat]);
Copy and paste the above line of code into the
addMarker()
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
const popup = marker.bindPopup(parseInt(xy[0]).toString() + ", " + parseInt(xy[1]).toString());
Update your
popup
statement to so that it matches the above snippet
NOTE: You will notice that there are some unfamiliar bits of code using square brackets [ ]
in this section of the practical - we will be looking at these later in the course, so feel free 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! We’ll be out of the desert of dispair in no time…
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.