Resource Loading
Document / Resource Loading: Resource Loading
What is Resource Loading in JavaScript?
View Answer:
What two events can we use to track the loading of external resources?
View Answer:
let img = new Image();
img.src = "https://example.com/some-image.jpg";
img.addEventListener('load', function() {
console.log('Image has loaded successfully');
});
img.addEventListener('error', function() {
console.error('An error occurred while loading the image');
});
Is it possible to load a function specified within an external script?
View Answer:
let script = document.createElement('script');
// can load any script, from any domain
script.src = 'https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.3.0/lodash.js';
document.head.append(script);
script.onload = function () {
// the script creates a variable "_"
console.log(_.VERSION); // shows library version
};
How are errors that occur during a script's loading tracked?
View Answer:
let script = document.createElement('script');
script.src = 'https://example.com/404.js'; // no such script
document.head.append(script);
script.onerror = function () {
console.log('Error loading ' + this.src); // Error loading https://example.com/404.js
};
Are there any limitations to the onload/onerror event properties?
View Answer:
The onload event triggers if a script is successfully loaded, even with programming mistakes. We can use the window.onerror global handler to track script errors.
Can load and error events be used with external resources or just internal scripts?
View Answer:
let img = document.createElement('img');
img.src = 'https://js.cx/clipart/train.gif'; // (*)
img.onload = function () {
console.log(`Image loaded, size ${img.width}x${img.height}`);
};
img.onerror = function () {
console.log('Error occurred while loading image');
};
What's a cross-origin policy in frontend web development?
View Answer:
A simple example is fetching data from a different domain.
fetch('https://api.different-domain.com/data')
.then(response => response.json())
.then(data => console.log(data))
.catch(error => console.log('An error occurred:', error));
In this case, the server https://api.different-domain.com
must include the appropriate CORS headers to allow the request. The server could respond with headers like:
Access-Control-Allow-Origin: https://your-domain.com
This tells the browser that it's okay to make a request from https://your-domain.com
to https://api.different-domain.com
.
However, without the appropriate server configuration, the CORS policy will block the request. You would see an error in your browser's console along the lines of:
Access to fetch at 'https://api.different-domain.com/data' from origin 'https://your-domain.com' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
That's why it's important to properly set up your server's CORS policy when you expect to serve resources to different domains.
Note: This is a simplified explanation. The actual CORS policy and implementation can be more complex and involves other headers as well, like Access-Control-Allow-Methods
, Access-Control-Allow-Headers
, Access-Control-Max-Age
, etc.
Are there any limitations in cross-origin error handling?
View Answer:
window.onerror = function(message, url, lineNo, colNo, error) {
console.log('Error:', message, 'Script:', url, 'Line:', lineNo, 'Column:', colNo, 'Error object:', error);
return true;
};
var script = document.createElement('script');
script.src = "https://different-domain.com/some-script.js";
document.body.appendChild(script);
If the script at "https://different-domain.com/some-script.js" encounters an error and the server doesn't allow for proper CORS configuration, your onerror
handler would log something like:
Error: Script error. Script: Line: 0 Column: 0 Error object: null
To handle cross-origin errors effectively, you need to set the crossorigin
attribute on the script tag and ensure the server responds with appropriate CORS headers.
Here's how you can modify the script tag:
window.onerror = function(message, url, lineNo, colNo, error) {
console.log('Error:', message, 'Script:', url, 'Line:', lineNo, 'Column:', colNo, 'Error object:', error);
return true;
};
var script = document.createElement('script');
script.src = "https://different-domain.com/some-script.js";
script.crossOrigin = "anonymous"; // New line
document.body.appendChild(script);
Now, if the server includes Access-Control-Allow-Origin: *
(or the specific origin instead of *
) in its headers, you will receive full error details in your onerror
handler, even if the script is loaded from a different origin. This can greatly enhance your ability to debug cross-origin scripts.
Why do we need error details in relation to cross-origin error handling?
View Answer:
What are the three levels of cross-origin access?
View Answer:
<script>
window.onerror = function (message, url, line, col, errorObj) {
console.log(`${message}\n${url}, ${line}:${col}`);
};
</script>
<script
crossorigin="anonymous"
src="https://cors.javascript.info/article/onload-onerror/crossorigin/error.js"
></script>
This example uses the fetch
API to make a cross-origin request:
fetch('https://example.com/data', {
method: 'GET',
mode: 'cors',
credentials: 'include',
})
.then(response => response.json())
.then(data => console.log(data))
.catch(error => console.log('Error:', error));
This example makes a GET request to 'https://example.com/data'. The mode
is set to 'cors' to indicate a CORS request, and credentials
is set to 'include' to send cookies with the request if they exist.
What is lazy loading in JavaScript?
View Answer:
Here is an example of lazy loading in JavaScript using the Intersection Observer API, which is useful for loading images as they appear in the viewport:
// HTML
<img class="lazyload" data-src="image.jpg" alt="Lazy Loaded Image">
// JavaScript
document.addEventListener("DOMContentLoaded", function() {
var lazyImages = [].slice.call(document.querySelectorAll("img.lazyload"));
if ("IntersectionObserver" in window) {
let lazyImageObserver = new IntersectionObserver(function(entries, observer) {
entries.forEach(function(entry) {
if (entry.isIntersecting) {
let lazyImage = entry.target;
lazyImage.src = lazyImage.dataset.src;
lazyImage.classList.remove("lazyload");
lazyImageObserver.unobserve(lazyImage);
}
});
});
lazyImages.forEach(function(lazyImage) {
lazyImageObserver.observe(lazyImage);
});
} else {
// Fallback for browsers without IntersectionObserver support
}
});
In this script, an IntersectionObserver
is used to watch all images with the class lazyload
. When one of these images comes into the viewport, the src
attribute is replaced by the data-src
attribute, loading the image. Then, the image is removed from the observer's watch list.
What is preloading in JavaScript?
View Answer:
Preloading can be done in different ways in JavaScript, here's an example on how you could preload an image:
let img = new Image();
img.src = "image.jpg";
The browser will start loading the image in the background without rendering it, ready for when it's needed.
To preload a script, you can use the preload
attribute in a link
element:
<link rel="preload" href="script.js" as="script">
This tells the browser to start loading the script as soon as possible without executing it. It will be fetched as soon as the browser is idle.
And you can also use JavaScript to add this element:
let link = document.createElement('link');
link.rel = "preload";
link.href = "script.js";
link.as = "script";
document.head.appendChild(link);
Note: Ensure you're using preloading judiciously, as too much preloading can degrade the initial page load performance.
What does "async" do in terms of resource loading?
View Answer:
Here is a simple example of how you would use the async
attribute in HTML:
<!DOCTYPE html>
<html>
<body>
<h2>Async script example</h2>
<p id="demo">Hello</p>
<script async src="script.js"></script>
</body>
</html>
In this HTML document, the script with the source "script.js" is loaded asynchronously. This means the HTML rendering isn't blocked by the loading of the script, improving page load time. However, the script will execute immediately when available, potentially before the HTML document is fully parsed.
The script.js file could be something like:
document.getElementById("demo").innerHTML = "Hello JavaScript!";
This would change the text in the paragraph with id "demo" to "Hello JavaScript!" once the script is loaded and executed.
What does "defer" do in JavaScript?
View Answer:
Here's an example of how you would use the defer
attribute in HTML:
<!DOCTYPE html>
<html>
<body>
<h2>Defer script example</h2>
<p id="demo">Hello</p>
<script defer src="script.js"></script>
</body>
</html>
In this HTML document, the script with the source "script.js" is loaded while the document is being parsed, but it does not run until after the document has finished parsing.
The script.js file might contain something like:
document.getElementById("demo").innerHTML = "Hello JavaScript!";
This would change the text in the paragraph with id "demo" to "Hello JavaScript!" once the script is executed, which will be after the HTML document has been fully parsed.
What is the difference between "async" and "defer"?
View Answer:
What is the significance of the rel="preload" attribute?
View Answer:
Here is an example of how you can use the rel="preload"
attribute in HTML:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Preload Example</title>
<link rel="preload" href="style.css" as="style">
<link rel="preload" href="script.js" as="script">
</head>
<body>
<h1>Welcome to my website!</h1>
<script src="script.js"></script>
</body>
</html>
In this HTML document, the styles from "style.css" and the JavaScript from "script.js" are preloaded. This means the browser begins downloading these resources as soon as possible, even before they are requested by the <script>
and <link>
tags. This can improve performance if these resources are needed soon after the HTML starts parsing.
Note that preloaded resources need to be consumed by a matching resource request in your JavaScript or CSS, otherwise they may be fetched twice.
How does browser caching impact resource loading?
View Answer:
How does HTTP/2 affect resource loading?
View Answer:
What's the impact of Content Delivery Networks (CDNs) on resource loading?
View Answer:
<!DOCTYPE html>
<html>
<head>
<title>My Web Page</title>
<!-- Using Google CDN to load jQuery -->
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
</head>
<body>
<button id="clickMe">Click Me!</button>
<script>
$(document).ready(function() {
$('#clickMe').click(function() {
console.log('You clicked the button!');
});
});
</script>
</body>
</html>
What is the role of the 'load' event in resource loading?
View Answer:
Here is a simple example showing how to use the load
event in JavaScript:
window.addEventListener('load', function() {
console.log('All resources finished loading!');
});
In this code, an event listener is added to the window
object, which fires when the load
event is triggered. The load
event occurs when the whole webpage (including assets like images and scripts) have finished loading. When this event is triggered, it executes the function, logging 'All resources finished loading!' to the console.
How can service workers improve resource loading?
View Answer:
Here's a simple example of a service worker in JavaScript. This script should be in a separate file, often named sw.js
or similar:
// Install event - cache files
self.addEventListener('install', function(event) {
event.waitUntil(
caches.open('my-cache').then(function(cache) {
return cache.addAll([
'/',
'/index.html',
'/style.css',
'/script.js',
'/image.jpg',
]);
})
);
});
// Fetch event - respond with cache or fetch
self.addEventListener('fetch', function(event) {
event.respondWith(
caches.match(event.request).then(function(response) {
return response || fetch(event.request);
})
);
});
In the first part, when the service worker is installed, it opens a cache called 'my-cache' and adds files to it. This means these files will be available offline and will load faster because they're coming from the cache.
In the second part, for every fetch request, the service worker first checks if the requested resource is in the cache. If it is, it returns that, otherwise, it fetches the resource from the network.
To register this service worker, you would add this to your main JavaScript file or inline script:
if ('serviceWorker' in navigator) {
navigator.serviceWorker.register('/sw.js').then(function(registration) {
console.log('Service Worker registered with scope:', registration.scope);
}).catch(function(error) {
console.log('Service Worker registration failed:', error);
});
}
This checks if the browser supports service workers, and if it does, it registers the service worker located at /sw.js
.
What is HTTP caching?
View Answer:
HTTP caching is typically handled on the server-side within HTTP headers. Here is an example of how it might look in an HTTP response:
HTTP/1.1 200 OK
Date: Sat, 04 Jun 2023 16:00:00 GMT
Content-Type: text/html
Content-Length: 1576
Last-Modified: Sat, 04 Jun 2023 15:00:00 GMT
ETag: "30a7-591fa93355a80"
Cache-Control: public, max-age=3600
Expires: Sat, 04 Jun 2023 17:00:00 GMT
Accept-Ranges: bytes
In this example, the Cache-Control
directive indicates the resource is public and can be cached, and that the maximum age of the cached resource is 3600 seconds (or one hour). After one hour, the cache is considered stale and a new copy of the resource will be fetched.
The Expires
header also specifies a date and time for when the cached copy should expire. This is an older method of controlling caches, and is often used as a fallback if Cache-Control
is not supported.
The ETag
and Last-Modified
headers are used for validation. When the cache is stale, the browser can send these values back to the server in a conditional request to check if the resource has changed. If it hasn't, the server can respond with a 304 Not Modified
status to tell the browser to reuse the cached copy, saving bandwidth.
How do 'ETags' help with resource loading?
View Answer:
Here is a very simplified example of a Node.js server using Express.js framework which utilizes the etag functionality.
const express = require('express');
const app = express();
const port = 3000;
app.get('/some-resource', (req, res) => {
// This could be a database call or some I/O operation in a real-world scenario
let data = {
id: 1,
name: "John Doe",
email: "johndoe@example.com"
};
// Compute an ETag for the data (you might use a different method in reality)
const etag = require('crypto').createHash('md5').update(JSON.stringify(data)).digest('hex');
// Check if the client sent an 'If-None-Match' header, and if it matches the ETag, send a '304 Not Modified' response
if (req.header('If-None-Match') === etag) {
res.sendStatus(304);
} else {
// Include the ETag in the response
res.setHeader('ETag', etag);
res.json(data);
}
});
app.listen(port, () => {
console.log(`App listening at http://localhost:${port}`)
});
What is the role of headers like 'Cache-Control' in resource loading?
View Answer:
Here's a simple Node.js server example using Express.js that serves static files with specific Cache-Control
directives.
const express = require('express');
const path = require('path');
const app = express();
const port = 3000;
// Set up static file serving
app.use(express.static(path.join(__dirname, 'public'), {
setHeaders: (res, path) => {
// Set Cache-Control header
res.setHeader('Cache-Control', 'public, max-age=86400'); // Cache for 24 hours (86400 seconds)
}
}));
app.listen(port, () => {
console.log(`App listening at http://localhost:${port}`)
});
In this example, any static files served from the public
directory will include the Cache-Control: public, max-age=86400
header in the response. This tells clients that they're allowed to publicly cache the resource and that the resource is considered fresh for 24 hours (86400 seconds). After that, the client needs to check back with the server to see if the resource has been updated.
Please note that different types of resources might need different caching strategies. For example, you might want to cache images, CSS, and JavaScript files for a longer period because they might not change frequently, but HTML files could be cached for a shorter period or not cached at all if they are dynamic and change frequently.
What is the purpose of the 'Expires' header?
View Answer:
Below is a simple example of a Node.js server using the Express.js framework that serves a static file with the Expires
header:
const express = require('express');
const path = require('path');
const app = express();
const port = 3000;
app.get('/some-file', function(req, res) {
// Set the Expires header
let oneDay = 24 * 60 * 60 * 1000; // hours*minutes*seconds*milliseconds
let oneDayFromNow = new Date(Date.now() + oneDay);
res.setHeader('Expires', oneDayFromNow.toUTCString());
// Send the file
res.sendFile(path.join(__dirname, 'some-file.txt'));
});
app.listen(port, () => {
console.log(`App listening at http://localhost:${port}`);
});
In this example, when a GET request is made to "/some-file", the server responds with the contents of "some-file.txt" and sets the Expires
header to one day from the current time. This means that the client can cache this file and consider it fresh without needing to check with the server again until that time has elapsed.
Note that HTTP/1.1 introduced the Cache-Control
header, which offers more fine-grained control over caching and is generally preferred over Expires
. However, Expires
is still useful for HTTP/1.0 compatibility.
What is the '304 Not Modified' response?
View Answer:
Here's an example using a Node.js server and the Express.js framework.
const express = require('express');
const app = express();
const port = 3000;
let data = {
id: 1,
name: "John Doe",
email: "johndoe@example.com",
lastModified: new Date() // Initial last modification time
};
app.get('/data', (req, res) => {
// Check if 'If-Modified-Since' header is set in the request
if (req.header('If-Modified-Since')) {
let ifModifiedSinceDate = new Date(req.header('If-Modified-Since'));
// If the data hasn't been modified since the date provided, return a '304 Not Modified' response
if (data.lastModified <= ifModifiedSinceDate) {
return res.status(304).send();
}
}
// Otherwise, include the 'Last-Modified' header in the response and send the data
res.setHeader('Last-Modified', data.lastModified.toUTCString());
res.json(data);
});
app.listen(port, () => {
console.log(`App listening at http://localhost:${port}`);
});