This project involved making a number of API calls which return JSON data asynchronously. These were handled within asynchronous calls...
export const getBasicInfo = (postcode) => {
return fetch(`https://api.postcodes.io/postcodes/${postcode}`)
.then((response) => {
// ...
}
The project made extensive use of arrow functions as callbacks which were called when the API calls returned their payload.
Promises were chained together to decode and display data from the API calls...
.then((json) => {
userInfoContainer.style.display = 'none';
crimeDataJson.push(json);
crimeDataJson = crimeDataJson.flat(2);
this.#createCrimeDataFromJson(crimeDataJson);
});
The JavaScript fetch() function was used throughout...
export const getLongAndLat = (postcode) => {
return fetch(`https://api.postcodes.io/postcodes/${postcode}`)
.then((response) => {
if (response.ok) {
return response.json().then((data) => ({
latitude: data.result.latitude,
longitude: data.result.longitude,
}));
} else {
throw new Error(response.status);
}
})
.catch((error) => console.log(error));
};
Options for API calls were encoded into query strings for all of the simple APIs used. None required authentication credentials.
Crime data is served up a month at a time by the crime data API. This app bundled 12 months of fetches into an array and then iterated through each call using the Arrap.map() function. This enabled the app to update the user with a progress bar as each month's data was received:
allQueryStrings.map((qs) =>
fetch(qs)
.then((response) => {
if (response.ok) {
progressBar.value = Math.floor(
(++fetchNumber / totalFetches) * 100
);
progressBar.textContent = `${progressBar.value}%`;
p.textContent = `Fetching crime data (${progressBar.value}%)`;
return response.json();
}
}).catch((error) => {
console.error("OH NO! This happened when fetching crime data:\n" + error);
userInfoContainer.style.display = 'none';
})
)
The project used the Array.reduce() method to summarise crime data according to any categories such as 'crime type' or 'data of crime':
const summary = allCrimes.reduce((tally, allCrimes) => {
if (tally[allCrimes[field]]) {
tally[allCrimes[field]] += 1;
} else {
tally[allCrimes[field]] = 1;
}
return tally;
}, {});
return summary;
Access to DOM nodes was used extensively to update data on the page.
const crimeInfoOutput = document.querySelector('#output__crime');
const crimeChartOverlay = document.querySelector('#crime__chart--large');
This was also used extensively within the project...
const pieChartHeading = document.createElement('h3');
pieChartHeading.textContent = pieChartTitle;
const barChartHeading = document.createElement('h3');
barChartHeading.textContent = barChartTitle;
const pieChartDescription = document.createElement('p');
pieChartDescription.id = 'pie-desc';
pieChartDescription.className = 'screen-reader-only';
pieChartDescription.innerText = getPieChartDescription(crimes);
// ...
crimeInfoOutput.append(crimeInfoDiv);
crimeInfoOutput.append(barChartHeading);
crimeInfoOutput.append(barChart);
crimeInfoOutput.append(barChartDescription);
crimeInfoOutput.append(pieChartHeading);
crimeInfoOutput.append(pieChart);
crimeInfoOutput.append(pieChartDescription);
This was used for example in enabling the user to click on a data chart graphic to zoom it to full screen...
function toggleChartToFullScreen(image) {
crimeChartOverlay.style.backgroundImage = 'url(' + image.src + ')';
crimeChartOverlay.style.display = 'block';
}
The site was constructed with a mobile-first design methodology. It was optimised for small screens and then enhanced for larger viewports...
/* Set all 'width-<size>' CSS classes to 90% of viewport width by default
e.g. .width-small, .width-medium, .width-large ... */
[class*='width-'] {
max-width: 90vw;
}
/* Override screen width mobile-first settings for larger viewports */
@media only screen and (min-width: 821px) {
.width-small {
max-width: 20rem;
}
.width-medium {
max-width: 40rem;
}
.width-large {
max-width: 60rem;
}
Some use was made of css layout primitives to aid consistent and structured styling...
.stack-s > * + * {
margin-top: 0.5rem;
}
.stack-m > * + * {
margin-top: 1.5rem;
}
.stack-xl > * + * {
margin-top: 4rem;
}
In-browser debugging was employed throughout development, for example to peek into JS object data.
Debugging through the following methods was used... console.log() console.table() console.error()