DOCTYPE
DOCTYPE is an abbreviation for Document Type. A DOCTYPE is always associated to a DTD - for Document Type Definition.
A DTD defines how documents of a certain type should be structured (i.e. a
button can contain a span but not a div), whereas a DOCTYPE declares what
DTD a document supposedly respects (i.e. this document respects the HTML DTD).
For webpages, the DOCTYPE declaration is required. It is used to tell user agents what version of the HTML specifications your document respects. Once a user agent has recognized a correct DOCTYPE, it will trigger the no-quirks mode matching this DOCTYPE for reading the document. If a user agent doesn’t recognize a correct DOCTYPE, it will trigger the quirks mode.
The DOCTYPE declaration for the HTML5 standards is <!DOCTYPE html>.
Building blocks of HTML5
- Semantics: HTML tags describe the content.
- Styling: Customizing appearance of HTML tags
- Connectivity: Communicate with the server in new and innovative ways.
- Offline and storage: Allows webpages to store data on the client-side locally and operate offline more efficiently.
- Multimedia: Makes video and audio first-class citizens in the Open Web.
- 2D/3D graphics and effects: Allows a much more diverse range of presentation options.
- Performance and integration: Provides greater speed optimization and better usage of computer hardware.
- Device access: Allows for the usage of various input and output devices.
HTML specification vs browser implementation
HTML specifications such as HTML5 define a set of rules that a document must
adhere to in order to be “valid” according to that specification. In addition, a
specification provides instructions on how a browser must interpret and render
such a document.
A browser is said to “support” a specification if it handles valid documents
according to the rules of the specification. As of yet, no browser supports all
aspects of the HTML5 specification (although all of the major browser support
most of it), and as a result, it is necessary for the developer to confirm
whether the aspect they are making use of will be supported by all of the
browsers on which they hope to display their content. This is why cross-browser
support continues to be a headache for developers, despite the improved
specificiations.
-
HTML5defines some rules to follow for an invalidHTML5document (i.e., one that contains syntactical errors) -
However, invalid documents may contain anything, so it’s impossible for the specification to handle all possibilities comprehensively.
-
Thus, many decisions about how to handle malformed documents are left up to the browser.
<header>, <article>,<section>, <footer> elements
-
<header>is used to contain introductory and navigational information about a section of the page. This can include the section heading, the author’s name, time and date of publication, table of contents, or other navigational information. -
<article>is meant to house a self-contained composition that can logically be independently recreated outside of the page without losing its meaning. Individual blog posts or news stories are good examples. -
<section>is a flexible container for holding content that shares a common informational theme or purpose. -
<footer>is used to hold information that should appear at the end of a section of content and contain additional information about the section. Author’s name, copyright information, and related links are typical examples of such content. -
Other semantic elements are
<form>and<table>
Multiple <header> elements
Yes to both. The W3 documents state that the tags represent the
header(<header>) and footer(<footer>) areas of their nearest ancestor
“section”. So not only can the page <body> contain a header and a footer, but
so can every <article> and <section> element.
-
W3 recommends having as many as you want, but only 1 of each for each “section” of your page, i.e. body, section etc.
attribute vs property
Attributes
Attributes are defined in the HTML markup and provide initial values for elements. They are static and do not change once the page is loaded unless explicitly modified using JavaScript.
<input type="text" value="initial value" />In this example, value="initial value" is an attribute.
Properties
Properties are part of the DOM and represent the current state of an element. They are dynamic and can change as the user interacts with the page or through JavaScript.
const inputElement = document.querySelector("input");
console.log(inputElement.value); // Logs the current value of the input element
inputElement.value = "new value"; // Changes the current value of the input elementIn this example, value is a property of the inputElement object.
Key differences
- Initialization: Attributes initialize DOM properties.
- State: Attributes are static, while properties are dynamic.
- Access: Attributes can be accessed using
getAttributeandsetAttributemethods, while properties can be accessed directly on the DOM object.
Example
<input id="myInput" type="text" value="initial value" />const inputElement = document.getElementById("myInput");
// Accessing attribute
console.log(inputElement.getAttribute("value")); // "initial value"
// Accessing property
console.log(inputElement.value); // "initial value"
// Changing property
inputElement.value = "new value";
console.log(inputElement.value); // "new value"
console.log(inputElement.getAttribute("value")); // "initial value"In this example, changing the value property does not affect the value
attribute.
alt attribute
The alt attribute provides alternative information for an image if a user
cannot view it. The alt attribute should be used to describe any images except
those which only serve a decorative purpose, in which case it should be left
empty.
-
Decorative images should have an empty
altattribute. -
Web crawlers use
alttags to understand image content, so they are considered important for Search Engine Optimization (SEO). -
Put the
.at the end ofalttag to improve accessibility.
srcset attribute
You would use the srcset attribute when you want to serve different images to
users depending on their device display width - serve higher quality images to
devices with retina display enhances the user experience while serving lower
resolution images to low-end devices increase performance and decrease data
wastage (because serving a larger image will not have any visible difference).
For example:
<img srcset="small.jpg 500w, medium.jpg 1000w, large.jpg 2000w" src="..." alt="">
tells the browser to display the small, medium or large .jpg graphic depending
on the client’s resolution. The first value is the image name and the second is
the width of the image in pixels. For a device width of 320px, the following
calculations are made:
- 500 / 320 = 1.5625
- 1000 / 320 = 3.125
- 2000 / 320 = 6.25
If the client’s resolution is 1x, 1.5625 is the closest, and 500w
corresponding to small.jpg will be selected by the browser.
If the resolution is retina (2x), the browser will use the closest resolution above the minimum. Meaning it will not choose the 500w (1.5625) because it is greater than 1 and the image might look bad. The browser would then choose the image with a resulting ratio closer to 2 which is 1000w (3.125).
srcsets solve the problem whereby you want to serve smaller image files to
narrow screen devices, as they don’t need huge images like desktop displays do —
and also optionally that you want to serve different resolution images to high
density/low-density screens.
data- attributes
Before JavaScript frameworks became popular, developers used data- attributes
to store extra data within the DOM itself, without other hacks such as
non-standard attributes, extra properties on the DOM. It is intended to store
custom data private to the page or application, for when there are no more
appropriate attributes or elements.
Another common use case for data- attributes is to store information used by
third-party libraries or frameworks. For example, the Bootstrap library uses
data attributes to cause <button>s to trigger actions on a modal elsewhere on
the page
(example).
<button type="button" data-bs-toggle="modal" data-bs-target="#myModal">
Launch modal
</button>
...
<div class="modal fade" id="myModal">Modal contents</div>These days, using data- attributes is generally not encouraged. One reason is
that users can modify the data attribute easily by using “inspect element” in
the browser. The data model is better stored within JavaScript environment and
have them kept in-sync with the DOM via virtual DOM reconciliation or two-way
data binding possibly through a library or a framework.
However, one perfectly valid use of data attributes, is to add an identifier for
end-to-end testing frameworks (e.g. Playwright, Puppeteer, Selenium),
without adding classes or ID attributes just for tests which are primarily for
other purposes. The element needs a way to be selected and something like
data-test-id="my-element" is a valid way to do so without convoluting the
semantic markup otherwise.
rel="noopener" attribute
The rel="noopener" is an attribute used in <a> elements (hyperlinks). It
prevents pages from having a window.opener property, which would otherwise
point to the page from where the link was opened and would allow the page opened
from the hyperlink to manipulate the page where the hyperlink is.
-
rel="noopener"is applied to hyperlinks. -
rel="noopener"prevents opened links from manipulating the source page.
load event and document DOMContentLoaded event
TL;DR
document.addEventListener("DOMContentLoaded", function () {
console.log("DOM fully loaded and parsed");
});
window.addEventListener("load", function () {
console.log("Page fully loaded");
});DOMContentLoaded event
The DOMContentLoaded event is fired when the initial HTML document has been
completely loaded and parsed, without waiting for stylesheets, images, and
subframes to finish loading. This event is useful when you want to execute
JavaScript code as soon as the DOM is ready, without waiting for all resources
to be fully loaded.
document.addEventListener("DOMContentLoaded", function () {
console.log("DOM fully loaded and parsed");
});load event
The load event is fired when the entire page, including all dependent
resources such as stylesheets, images, and subframes, has finished loading. This
event is useful when you need to perform actions that require all resources to
be fully loaded, such as initializing a slideshow or performing layout
calculations that depend on image sizes.
window.addEventListener("load", function () {
console.log("Page fully loaded");
});Key differences
- Timing:
DOMContentLoadedfires earlier thanload.DOMContentLoadedoccurs after the HTML is fully parsed, whileloadwaits for all resources to be loaded. - Use cases: Use
DOMContentLoadedfor tasks that only require the DOM to be ready, such as attaching event listeners or manipulating the DOM. Useloadfor tasks that depend on all resources being fully loaded, such as image-dependent layout calculations.
mouseenter vs mouseover
TL;DR
mouseenter events do not bubble. The mouseenter event is triggered only when
the mouse pointer enters the element itself, not its descendants. If a parent
element has child elements, and the mouse pointer enters child elements, the
mouseenter event will not be triggered on the parent element again, it’s only
triggered once upon entry of parent element without regard for its contents. If
both parent and child have mouseenter listeners attached and the mouse pointer
moves from the parent element to the child element, mouseenter will only fire
for the child.
mouseover events bubble up the DOM tree. The mouseover event is triggered
when the mouse pointer enters the element or one of its descendants. If have a
parent element has child elements, and the mouse pointer enters child elements,
the mouseover event will be triggered on the parent element again as well. If
the parent element has multiple child elements, this can result in multiple
event callbacks fired. If there are child elements, and the mouse pointer moves
from the parent element to the child element, mouseover will fire for both the
parent and the child.
| Property | mouseenter | mouseover |
|---|---|---|
| Bubbling | No | Yes |
| Trigger | Only when entering itself | When entering itself and when entering descendants |
mouseenter event:
- Does not bubble: The
mouseenterevent does not bubble. It is only triggered when the mouse pointer enters the element to which the event listener is attached, not when it enters any child elements. - Triggered once: The
mouseenterevent is triggered only once when the mouse pointer enters the element, making it more predictable and easier to manage in certain scenarios.
A use case for mouseenter is when you want to detect the mouse entering an
element without worrying about child elements triggering the event multiple
times.
mouseover Event:
- Bubbles up the DOM: The
mouseoverevent bubbles up through the DOM. This means that if you have an event listener on a parent element, it will also trigger when the mouse pointer moves over any child elements. - Triggered multiple times: The
mouseoverevent is triggered every time the mouse pointer moves over an element or any of its child elements. This can lead to multiple triggers if you have nested elements.
A use case for mouseover is when you want to detect when the mouse enters an
element or any of its children and are okay with the events triggering multiple
times.
Example
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Mouse Events Example</title>
<style>
.parent {
width: 200px;
height: 200px;
background-color: lightblue;
padding: 20px;
}
.child {
width: 100px;
height: 100px;
background-color: lightcoral;
}
</style>
</head>
<body>
<div class="parent">
Parent Element
<div class="child">Child Element</div>
</div>
<script>
const parent = document.querySelector(".parent");
const child = document.querySelector(".child");
// Mouseover event on parent.
parent.addEventListener("mouseover", () => {
console.log("Mouseover on parent");
});
// Mouseenter event on parent.
parent.addEventListener("mouseenter", () => {
console.log("Mouseenter on parent");
});
// Mouseover event on child.
child.addEventListener("mouseover", () => {
console.log("Mouseover on child");
});
// Mouseenter event on child.
child.addEventListener("mouseenter", () => {
console.log("Mouseenter on child");
});
</script>
</body>
</html>Expected behavior
- When the mouse enters the parent element:
- The
mouseoverevent on the parent will trigger. - The
mouseenterevent on the parent will trigger.
- The
- When the mouse enters the child element:
- The
mouseoverevent on the parent will trigger again becausemouseoverbubbles up from the child. - The
mouseoverevent on the child will trigger. - The
mouseenterevent on the child will trigger. - The
mouseenterevent on the parent will not trigger again becausemouseenterdoes not bubble.
- The
event.preventDefault() vs event.stopPropagation()?
TL;DR
event.preventDefault()
event.preventDefault() is a method that cancels the event if it is cancelable,
meaning that the default action that belongs to the event will not occur. For
example, this can be used to prevent a form from being submitted:
document.querySelector("form").addEventListener("submit", function (event) {
event.preventDefault();
// Form submission is prevented
});event.stopPropagation()
event.stopPropagation() is a method that prevents the event from bubbling up
the DOM tree, stopping any parent handlers from being notified of the event.
This is useful when you want to handle an event at a specific level and do not
want it to trigger handlers on parent elements:
document.querySelector(".child").addEventListener("click", function (event) {
event.stopPropagation();
// Click event will not propagate to parent elements
});Key differences
event.preventDefault()stops the default action associated with the event.event.stopPropagation()stops the event from propagating (bubbling) up to parent elements.
Use cases
- Use
event.preventDefault()when you want to prevent the default behavior of an element, such as preventing a link from navigating or a form from submitting. - Use
event.stopPropagation()when you want to prevent an event from reaching parent elements, which can be useful in complex UIs where multiple elements have event listeners.
DOM
The DOM (Document Object Model) is a cross-platform API that treats HTML and XML documents as a tree structure consisting of nodes. These nodes (such as elements and text nodes) are objects that can be programmatically manipulated and any visible changes made to them are reflected live in the document. In a browser, this API is available to JavaScript where DOM nodes can be manipulated to change their styles, contents, placement in the document, or interacted with through event listeners.
- The DOM was designed to be independent of any particular programming language, making the structural representation of the document available from a single, consistent API.
- The DOM is constructed progressively in the browser as a page loads, which is
why scripts are often placed at the bottom of a page, in the
<head>with adeferattribute, or inside aDOMContentLoadedevent listener. Scripts that manipulate DOM nodes should be run after the DOM has been constructed to avoid errors. document.getElementById()anddocument.querySelector()are common functions for selecting DOM nodes.- Setting the
innerHTMLproperty to a new value runs the string through the HTML parser, offering an easy way to append dynamic HTML content to a node. - MDN docs for DOM
The DOM is structured as a hierarchical tree of nodes. Here are the main types of nodes:
- Document node: The root of the document tree. It represents the entire document.
- Element nodes: These represent HTML elements and form the bulk of the document tree.
- Attribute nodes: These are associated with element nodes and represent the attributes of those elements.
- Text nodes: These represent the text content within elements.
- Comment nodes: These represent comments in the HTML.
Example
<!DOCTYPE html>
<html>
<head>
<title>Document</title>
</head>
<body>
<h1>Hello, World!</h1>
<p>This is a paragraph.</p>
</body>
</html>Document
└── html
├── head
│ └── title
│ └── "Document"
└── body
├── h1
│ └── "Hello, World!"
└── p
└── "This is a paragraph."JavaScript can be used to access and manipulate the DOM. Here are some common methods:
document.getElementById(id): Selects an element by its ID.document.querySelector(selector): Selects the first element that matches a CSS selector.element.appendChild(node): Adds a new child node to an element.element.removeChild(node): Removes a child node from an element.
Example
// Select the <h1> element
const heading = document.querySelector("h1");
// Change its text content
heading.textContent = "Hello, DOM!";HTML5 web storage
With HTML5, web pages can store data locally within the user’s browser. The data is stored in name/value pairs, and a web page can only access data stored by itself.
Differences between localStorage and sessionStorage regarding lifetime:
- Data stored through
localStorageis permanent: it does not expire and remains stored on the user’s computer until a web app deletes it or the user asks the browser to delete it. sessionStoragehas the same lifetime as the top-level window or browser tab in which the data got stored. When the tab is permanently closed, any data stored throughsessionStorageis deleted.
Differences between localStorage and sessionStorage regarding storage
scope: Both forms of storage are scoped to the document origin so that
documents with different origins will never share the stored objects.
-
sessionStorageis also scoped on a per-window basis. Two browser tabs with documents from the same origin have separatesessionStoragedata. -
Unlike in
localStorage, the same scripts from the same origin can’t access each other’ssessionStoragewhen opened in different tabs. -
Earlier, this was done with cookies.
-
The storage limit is far larger (at least 5MB) than with cookies and its faster.
-
The data is never transferred to the server and can only be used if the client specifically asks for it.
Adding, removing, and modifying HTML elements using JavaScript
TL;DR
To add, remove, and modify HTML elements using JavaScript, you can use methods
like createElement, appendChild, removeChild, and properties like
innerHTML and textContent. For example, to add an element, you can create it
using document.createElement and then append it to a parent element using
appendChild. To remove an element, you can use removeChild on its parent. To
modify an element, you can change its innerHTML or textContent.
// Adding an element
const newElement = document.createElement("div");
newElement.textContent = "Hello, World!";
document.body.appendChild(newElement);
// Removing an element
const elementToRemove = document.getElementById("elementId");
elementToRemove.parentNode.removeChild(elementToRemove);
// Modifying an element
const elementToModify = document.getElementById("elementId");
elementToModify.innerHTML = "New Content";document.querySelector() vs document.getElementById()
TL;DR
document.querySelector() and document.getElementById() are both methods used
to select elements from the DOM, but they have key differences.
document.querySelector() can select any element using a CSS selector and
returns the first match, while document.getElementById() selects an element by
its ID and returns the element with that specific ID.
// Using document.querySelector()
const element = document.querySelector(".my-class");
// Using document.getElementById()
const elementById = document.getElementById("my-id");document.querySelector()
- Can select elements using any valid CSS selector, including class, ID, tag, attribute, and pseudo-classes
- Returns the first element that matches the specified selector
- More versatile but slightly slower due to the flexibility of CSS selectors
// Select the first element with the class 'my-class'
const element = document.querySelector(".my-class");
// Select the first <div> element
const divElement = document.querySelector("div");
// Select the first element with the attribute data-role='button'
const buttonElement = document.querySelector('[data-role="button"]');document.getElementById()
- Selects an element by its ID attribute
- Returns the element with the specified ID
- Faster and more efficient for selecting elements by ID, but less versatile
// Select the element with the ID 'my-id'
const elementById = document.getElementById("my-id");Key differences
- Selector type:
document.querySelector()uses CSS selectors, whiledocument.getElementById()uses only the ID attribute. - Return value:
document.querySelector()returns the first matching element, whereasdocument.getElementById()returns the element with the specified ID. - Performance:
document.getElementById()is generally faster because it directly accesses the element by ID, whiledocument.querySelector()has to parse the CSS selector.
innerHTML vs textContent
innerHTML
- Dynamically adding or updating HTML content
- Rendering HTML tags and elements
Using innerHTML can expose your application to Cross-Site Scripting (XSS)
attacks if you insert untrusted content. Always sanitize any user input before
setting it as innerHTML.
textContent
textContent is a property that allows you to get or set the text content of an
element. It ignores any HTML tags and renders them as plain text, making it
safer for inserting user-generated content.
Example
const element = document.getElementById("example");
element.textContent = "<strong>Bold Text</strong>"; // This will render as plain text: <strong>Bold Text</strong>- Safely inserting user-generated content
- Stripping HTML tags from a string
Performance considerations
textContent is generally faster than innerHTML because it does not parse and
render HTML tags. It simply updates the text content of the element.
document.write()
document.write() can be used to write content directly to the document during
the initial page load. This is one of the few scenarios where it might be
appropriate, as it can be simpler and faster for very basic tasks.
Example
<!DOCTYPE html>
<html>
<head>
<title>Document Write Example</title>
</head>
<body>
<script>
document.write("<h1>Hello, World!</h1>");
</script>
</body>
</html>-
Educational purposes:
document.write()is sometimes used in educational contexts to demonstrate basic JavaScript concepts. It provides a straightforward way to show how JavaScript can manipulate the DOM. -
Quick debugging:: For quick and dirty debugging,
document.write()can be used to output variables or messages directly to the document. However, this is not recommended for production code.Example
var debugMessage = "Debugging message"; document.write(debugMessage); -
Legacy code: In some older codebases, you might encounter
document.write(). While it’s not recommended to use it in new projects, understanding it can be useful for maintaining or refactoring legacy code. -
Why not use
document.write()?-
Overwrites the document: If called after the page has loaded,
document.write()will overwrite the entire document, which can lead to loss of content and a poor user experience. -
Better alternatives: Modern methods like
innerHTML,appendChild(), and frameworks like React or Vue provide more control and are safer to use.Example
// Using innerHTML document.getElementById("content").innerHTML = "<h1>Hello, World!</h1>"; // Using appendChild var newElement = document.createElement("h1"); newElement.textContent = "Hello, World!"; document.getElementById("content").appendChild(newElement);
-
How do <iframe> on a page communicate?
TL;DR
<iframe> elements on a page can communicate using the postMessage API. This
allows for secure cross-origin communication between the parent page and the
iframe. The postMessage method sends a message, and the message event
listener receives it. Here’s a simple example:
// In the parent page
const iframe = document.querySelector("iframe");
iframe.contentWindow.postMessage("Hello from parent", "*");
// In the iframe
window.addEventListener("message", (event) => {
console.log(event.data); // 'Hello from parent'
});The postMessage API is the most common and secure way for iframes to
communicate with each other or with their parent page. This method allows for
cross-origin communication, which is essential for modern web applications.
To send a message from the parent page to the iframe, you can use the
postMessage method. Here’s an example:
// In the parent page
const iframe = document.querySelector("iframe");
iframe.contentWindow.postMessage("Hello from parent", "*");In this example, the parent page selects the iframe and sends a message to it.
The second parameter, '*', is the target origin. It specifies the origin of
the target window. Using '*' means the message can be received by any origin,
but for security reasons, it’s better to specify the exact origin.
To receive a message in the iframe, you need to add an event listener for the
message event:
// In the iframe
window.addEventListener("message", (event) => {
console.log(event.data); // 'Hello from parent'
});The event object contains the data property, which holds the message sent by
the parent page.
When using postMessage, it’s crucial to consider security:
- Specify the target origin: Instead of using
'*', specify the exact origin to ensure that only messages from trusted sources are received. - Validate the message: Always validate the message content to prevent malicious data from being processed.
Example with a specified target origin
// In the parent page
const iframe = document.querySelector("iframe");
const targetOrigin = "https://example.com";
iframe.contentWindow.postMessage("Hello from parent", targetOrigin);
// In the iframe
window.addEventListener("message", (event) => {
if (event.origin === "https://parent.com") {
console.log(event.data); // 'Hello from parent'
}
});In this example, the parent page sends a message only to https://example.com,
and the iframe processes the message only if it comes from https://parent.com.
SEO friendly SPAs
A single page application (SPA) is a web application that interacts with the user by dynamically rewriting the current page rather than loading entire new pages from the server. This results in a more fluid user experience, similar to a desktop application.
- The application loads a single HTML page and dynamically updates it as the user interacts with the app
- Uses AJAX or Fetch API to communicate with the server and update the page without a full reload
- Often relies on client-side routing to manage different views or states within the app
Benefits
- Faster interactions after the initial load
- Reduced server load due to fewer full-page requests
- Improved user experience with smoother transitions
How to make an SPA SEO-friendly
SPAs can be challenging for SEO because search engines may not execute JavaScript to render content. This can result in search engines indexing an empty or incomplete page.
Server-side rendering (SSR)
Server-side rendering involves rendering the initial HTML of the page on the server before sending it to the client. This ensures that search engines can index the fully rendered content.
- React: Use Next.js, which provides built-in support for SSR
- Vue.js: Use Nuxt.js, which also supports SSR out of the box
Example with Next.js:
import React from "react";
import { GetServerSideProps } from "next";
const Page = ({ data }) => {
return (
<div>
<h1>{data.title}</h1>
<p>{data.content}</p>
</div>
);
};
export const getServerSideProps: GetServerSideProps = async () => {
const res = await fetch("https://api.example.com/data");
const data = await res.json();
return {
props: {
data,
},
};
};
export default Page;Static site generation (SSG)
Static site generation involves generating the HTML for each page at build time. This approach is suitable for content that doesn’t change frequently.
- React: Use Next.js with its static generation capabilities
- Vue.js: Use Nuxt.js with its static site generation feature
Example with Next.js:
import React from "react";
import { GetStaticProps } from "next";
const Page = ({ data }) => {
return (
<div>
<h1>{data.title}</h1>
<p>{data.content}</p>
</div>
);
};
export const getStaticProps: GetStaticProps = async () => {
const res = await fetch("https://api.example.com/data");
const data = await res.json();
return {
props: {
data,
},
};
};
export default Page;Pre-rendering with tools
Some tools can pre-render your SPA and serve the pre-rendered HTML to search engines.
- Prerender.io: A service that pre-renders your JavaScript application and serves the static HTML to search engines
- Rendertron: A headless Chrome rendering solution that can be used to pre-render your SPA
Further reading
Progressive rendering
Progressive rendering is the name given to techniques used to improve the performance of a webpage (in particular, improve perceived load time) to render content for display as quickly as possible.
It used to be much more prevalent in the days before broadband internet but it is still used in modern development as mobile data connections are becoming increasingly popular (and unreliable)!
Lazy loading of images
Images on the page are not loaded all at once. The image is only loaded when the user scrolls into/near the part of the page that displays the image.
<img loading="lazy">is a modern way to instruct the browser to defer loading of images that are outside of the screen until the user scrolls near them.- Use JavaScript to watch the scroll position and load the image when the image is about to come on screen (by comparing the coordinates of the image with the scroll position).
Prioritizing visible content (or above-the-fold rendering)
Include only the minimum CSS/content/scripts necessary for the amount of page
that would be rendered in the users browser first to display as quickly as
possible, you can then use deferred scripts or listen for the
DOMContentLoaded/load event to load in other resources and content.
Async HTML fragments
Flushing parts of the HTML to the browser as the page is constructed on the back end. More details on the technique can be found here.
Other modern techniques
Cache busting
Browsers have a cache to temporarily store files on websites so they don’t need to be re-downloaded again when switching between pages or reloading the same page. The server is set up to send headers that tell the browser to store the file for a given amount of time. This greatly increases website speed and preserves bandwidth.
However, it can cause problems when the website has been changed by developers because the user’s cache still references old files. This can either leave them with old functionality or break a website if the cached CSS and JavaScript files are referencing elements that no longer exist, have moved or have been renamed.
Cache busting is the process of forcing the browser to download the new files. This is done by naming the file something different to the old file.
A common technique to force the browser to re-download the file is to append a query string to the end of the file.
src="js/script.js"=>src="js/script.js?v=2"
The browser considers it a different file but prevents the need to change the file name.
Adding elements
To add an HTML element, you can use the document.createElement method to
create a new element and then append it to a parent element using appendChild.
// Create a new div element
const newDiv = document.createElement("div");
// Set its content
newDiv.textContent = "Hello, World!";
// Append the new element to the body
document.body.appendChild(newDiv);You can also use insertBefore to insert the new element before an existing
child element.
const parentElement = document.getElementById("parent");
const newElement = document.createElement("p");
newElement.textContent = "Inserted Paragraph";
const referenceElement = document.getElementById("reference");
parentElement.insertBefore(newElement, referenceElement);Removing elements
To remove an HTML element, you can use the removeChild method on its parent
element.
// Select the element to be removed
const elementToRemove = document.getElementById("elementId");
// Remove the element
elementToRemove.parentNode.removeChild(elementToRemove);Alternatively, you can use the remove method directly on the element.
const elementToRemove = document.getElementById("elementId");
elementToRemove.remove();Modifying elements
To modify an HTML element, you can change its properties such as innerHTML,
textContent, or attributes.
// Select the element to be modified
const elementToModify = document.getElementById("elementId");
// Change its inner HTML
elementToModify.innerHTML = "New Content";
// Change its text content
elementToModify.textContent = "New Text Content";
// Change an attribute
elementToModify.setAttribute("class", "new-class");You can also use methods like classList.add, classList.remove, and
classList.toggle to modify the element’s classes.
const element = document.getElementById("elementId");
// Add a class
element.classList.add("new-class");
// Remove a class
element.classList.remove("old-class");
// Toggle a class
element.classList.toggle("active");Multilingual sites
Designing and developing for multilingual sites is part of internationalization (i18n).
Serving a page with content available in multiple languages
Serving a page in different languages is one of the aspects of internationalization (i18n).
When an HTTP request is made to a server, the requesting user agent usually
sends information about language preferences, such as in the Accept-Language
header. The server can then use this information to return a version of the
document in the appropriate language if such an alternative is available. The
returned HTML document should also declare the lang attribute in the <html>
tag, such as <html lang="en">...</html>.
To let a search engine know that the same content is available in different
languages, <link> tags with the rel="alternate" and hreflang="..."
attributes should be used. E.g.
<link rel="alternate" hreflang="de" href="http://de.example.com/page.html" />.
- Server-side rendering: The HTML markup will contain string placeholders and content for the specific language will be fetched from configuration in code or a translation service. The server then dynamically generates the HTML page with content in that particular language.
- Client-side rendering: The appropriate locale strings will be fetched and combined with the JavaScript-based views.
Search Engine Optimization
- Use the
langattribute on the<html>tag. - Include the locale in the URL (e.g en_US, zh_CN, etc).
- Webpages should use
<link rel="alternate" hreflang="other_locale" href="url_for_other_locale">to tell search engines that there is another page at the specifiedhrefwith the same content but for another language/locale. - Use a fallback page for unmatched languages. Use the “x-default” value:
<link rel="alternate" href="url_for_fallback" hreflang="x-default" />.
Understanding the difference between locale vs language
Locale settings control how numbers, dates, and times display for your region: which may be a country, or a portion of country or may not even honor country boundaries.
Language can differ between countries
Certain languages, especially the widely-spoken languages have different “flavors” in different countries (grammar rules, spelling, characters). It’s important to differentiate languages for the target country and not assume/force one country’s version of a language for all countries which speak the language. Examples:
en:en-US(American English),en-GB(British English)zh:zh-CN(Chinese (Simplified)),zh-TW(Chinese (Traditional))
Predict locale but don’t restrict
Servers can determine the locale/language of visitors via a combination of HTTP
Accept-Language headers and IPs. With these, visitors can automatically select
the best locale for the visitor. However, predictions are not foolproof
(especially if visitors are using VPNs) and visitors should still be allowed to
change their country/language easily without hassle.
Consider differences in the length of text in different languages
Some content can be longer when written in another language. Be wary of layout or overflow issues in the design. It’s best to avoid designing where the amount of text would make or break a design. Character counts come into play with things like headlines, labels, and buttons. They are less of an issue with free-flowing text such as body text or comments. For example, some languages, such as German and French, tend to use longer words and sentences than English, which can cause layout issues if you do not take this into account.
Language reading direction
Languages like English and French are written from left-to-right, top-to-bottom. However some languages, such as Hebrew and Arabic, are written from right to left. This can affect the layout of your site and the placement of elements on the page, so you must be careful to design your site in a way that accommodates different text directions.
Do not concatenate translated strings
Do not do anything like "The date today is " + date. It will break in
languages with different word order. Use a template string with parameters
substitution for each language instead. For example, look at the following two
sentences in English and Chinese respectively: I will travel on {% date %} and
我会在{% date %}出发. Note that the position of the variable is different due
to grammar rules of the language.
Formatting dates and currencies
Calendar dates are sometimes presented in different ways. Eg. “May 31, 2012” in the U.S. vs. “31 May 2012” in parts of Europe.
Do not put text in images
Putting text in raster-based images (e.g. png, gif, jpg, etc.), is not a scalable approach. Placing text in an image is still a popular way to get good-looking, non-system fonts to display on any computer. However, to support image text translation other languages, there needs to be a separate image created for each language which is not a scalable workflow for designers.
Be mindful of how colors are perceived
Colors are perceived differently across languages and cultures. The design should use color appropriately.