JavaScript for Websites
JavaScript is used since Web 2.0 to add interactivity to web pages:
- ๐ Validate a form (errors, autocompletion, ...)
- ๐ Handling events (click, visibility change, ...)
- ๐ Connect to an API (fetch...)
- ๐ฅ For a programmer, it can be used on someone else's website, to automate a task, bypass a restriction...
- ...
Some browsers may not support some newly added features.
- ๐งช See caniuse to see which browsers support a specific feature
- ๐ If there is a heavy need for a feature, you can use polyfill to load missing features. You can generate one here.
JavaScript (JS) is based on ECMAScript. ECMAScript2016 (ES6) is fully supported since late 2016/early 2017, while for ES6+, providers/vendors are implementing the features they want.
Getting started
There are 3 ways to use JavaScript for a website.
Inline JS ๐คฎ | SCRIPT tag ๐คข | External JavaScript file ๐ |
---|---|---|
You can use attributes such as
|
You can use
|
You can link an external file. This is the proper way to do it (CSP policy+caching, CDNs, SoC...).
โ ๏ธ Note: Modern browsers usually cache JavaScript files. If you changed a file, and don't see any changes, reload the page while ignoring the cache: CTRL+SHIFT+R or SHIFT+F5 or CTRL+F5. |
Document Object Model
There is one big difference with usual JavaScript: there is a variable window
for the tab in which the page is displayed.
The Document Object Model (DOM) is a tree of nodes (HTML tags). The root is window.document
or the shortcut window.document
.
document.head // access the head tag
document.body // access the body tag
// collection with every
document.forms // form
document.images // image
document.links // link
document.scripts // scripts
// ๐ฅ tip: collection to Array
Array.from(document.links)
To create a node, we use these. Note that by editing the innerHTML of an existing tag manually, you can avoid doing that.
const node = document.createTextNode("Some text")
const node = document.createElement("div")
To find a node, you can use
const xxx = document // you can put any Node here
// ex: only match <node id="id" />
const node = xxx.getElementById('id');
// ex: match <node class="a b c" />
const nodeList = xxx.getElementsByClassName('c');
// ex: match every link
const nodeList = xxx.getElementsByTagName('a');
// ๐ฅ You can use CSS selectors
const node = xxx.querySelector('#id');
const nodeList = xxx.querySelectorAll('table tr > .xxx');
// Navigate from one node to another
xxx.childNodes ; xxx.children
xxx.firstChild ; xxx.firstElementChild
xxx.lastChild ; xxx.lastElementChild
xxx.parentNode ; xxx.parentElement
// edit HTML
xxx.replaceChildren() // remove children
xxx.replaceChildren(node1, node2) // replace with ...
xxx.appendChild(node)
// edit attributes
xxx.hasAttribute('class')
xxx.getAttribute('class')
xxx.removeAttribute("hidden")
xxx.setAttribute("hidden", "")
xxx.classList.xxx("yyy") // add, remove, replace...
// edit content
xxx.innerHTML = `<p>You can write HTML</p>`
xxx.innerText = "<p>HTML won't be rendered</p>"
xxx.textContent = "<p>HTML won't be rendered</p>"
// other
xxx.outerHTML
xxx.nodeName // "HEAD"...
xxx.click() // fire a "click" event
You can also manually edit the style
xxx.style.width = "5px";
xxx.style.display = "none";
xxx.style.color = "red";
xxx.style.backgroundColor = "yellow";
// get effective style
const style = window.getComputedStyle(xxx)
if (style.visibility === 'hidden') { /* ..; */ }
๐ Events ๐
Listen to events
<!-- โ inside the HTML. Use this to get the button. -->
<button onclick="console.log(event, this)">xxx</button>
// โ
inside the JavaScript
const btn = document.querySelector("...")
// only to add one listener
btn.onclick = (event) => { console.log(event, btn) }
// add as many listeners as you want
btn.addEventListener('click', (event) => { console.log(event, btn) })
Add on
before the name, like click
would become onclick
to get the HTML attribute from a JSEvent.
click
: called when we clicked on the componentload
: called when the component was loadederror
: called on loading error (ex: loading image failed)mouseover
: trigger function on mouse enteringmouseout
: trigger function on mouse exiting- See: HTML events and Global attributes
Useful attributes/methods to call on an event are
event.preventDefault()
: don't further handle the event (ex: stop a link from opening another page, a form from being submitted...)event.target
: the element on which we clicked/...
Examples
Execute code when the visibility (hidden, shown back) of the page is changed (ex: tab changed, minified...)
// โก๏ธ See "document.hidden" (boolean)
document.addEventListener('visibilitychange', () => {});
Execute some code when a key is pressed
document.onkeydown = e => {
switch (e.code) {
case 'KeyR':
e.preventDefault();
// ...
break;
}
}
๐ Validating Forms ๐
You can listen to a form submission with submit
/onsubmit
. This event is particular, as true must be returned if we can send the form, false otherwise. It's used to validate a form, before sending it.
const form = [...]
form.onsubmit = (event) => {
const form = event.target // if not stored
// ๐ค using elements (array)
const value = form.elements['name']['value'] // get
// โ
Using FormData
let formData = new FormData(form);
if (formData.has('name')) {}
formData.append('name', 'value') // add
formData.set('name', 'value') // set
const value = formData.get('name') // get
for(const [name, value] of formData) {} // iterate
// ...
return false // false <=> don't send
}
You may know that you can validate an HTML form with attributes such as minlength
... You can show custom errors, or further handle the verification in JavaScript.
<form novalidate></form> <!-- disable HTML errors -->
form.onsubmit = (event) => {
const form = document.forms[0] // alternative
const xxx = form.querySelector('#xxx')
if (xxx.validity.badInput) {}
if (xxx.validity.tooShort) {}
if (xxx.validity.tooLong) {}
if (xxx.validity.typeMismatch) {}
if (xxx.validity.valueMissing) {}
// ...
}
โ Request an API โ
*See also, APIs, and Async, await, and Promises
The Fetch API is the easiest, and most used to do API requests.
// you can use a try-catch to catch errors
const res = await fetch("URL" , params)
// or
fetch('URL', params)
.then(res => res.xxx())
.then(xxx => /* ... */)
.catch(err => /* ... */)
Note that res
is a pending request, you need to ask for an output.
const json = await res.json() // as a json object
const text = await res.text() // as a string
// other
const value = res.headers.get('header-name')
GET is the default if no parameters are provided.
const params = { method: "GET", }
POST/PUT/PATCH are usually taking a body.
โก๏ธ Note that the API may not require you to add headers.
const params = {
method: "POST",
body: JSON.stringify({ username: "xxx" }),
headers: { 'Content-type': 'application/json; charset=UTF-8' }
}
const params = {
method: "POST",
body: 'xxx=value&yyy=value',
headers: { 'Content-type': 'application/x-www-form-urlencoded' }
}
Prior to 2015: XMLHTTPRequest
To code a simplified fetch
, you could use the code below.
function fetchXXX(URL, params = undefined) {
return new Promise((resolve, reject) => {
const ajax = new XMLHttpRequest();
ajax.onreadystatechange = () => {
// we the loading is done
if(ajax.readyState === XMLHttpRequest.DONE) {
// 200, 201, ... are OK
if (ajax.status >= 200 && ajax.status < 300)
resolve(ajax.responseText);
else
reject(ajax.responseText);
}
}
// use the provided method, or GET by default
ajax.open(params?.method ?? 'GET', URL, true);
// add headers
for(const header in params?.headers) {
ajax.setRequestHeader(header, params?.headers[header]);
}
// send the body, if any
ajax.send(params?.body);
})
}
// tested with GET+POST,
// but the code is using a somewhat recent syntax
fetchXXX('https://jsonplaceholder.typicode.com/posts')
.then((text) => console.log(text));
โก Special features โก
Notifications
if (Notification?.permission === "granted") {
// send a notification
const img = 'xxx.png';
const notification = new Notification(title, { body: body, icon: img });
// done
} else {
// you may request permission first
Notification.requestPermission().then()
}
LocalStorage
The localStorage is a storage are inside the client browser. What's stored inside can be edited inside the devtools, in the "Application" tab.
// store
localStorage.setItem('key', 'value')
// get
const value = localStorage.getItem("key")
if (value != null) { /* ... */ }
// remove
localStorage.removeItem("key")
// clear
localStorage.clear()
It's useful to store/cache some data, but you must take into account that the user can add/edit/remove data.
โก๏ธTo store an Object, use JSON.stringify
and JSON.parse
.
Popups
- โก๏ธ Show a popup with some message
alert("XXX?")
- โก๏ธ Show a popup asking the user for input
let value = prompt("XXX?", "defaultValueOfTheInputField")
if (value != null) {
// ...
}
Libraries
- anime.js (animations, 44k โญ)
- typed.js (typing, 12.6k โญ)
- bideo.js (video, 4.3k โญ)
- particles.js (show particules, 26.6k โญ)
- GSAP (animations, 15.4k โญ)
- createjs (libraries)
- turn.js (book-like, 6.8k โญ). See also StPageFlip / pdf-flipbook.
- d3.js (plotting..., 104k โญ)
jQuery
A long time ago, jQuery was a popular library shortening/simplifying DOM manipulations. Nowadays, many features were added to JavaScript, and with technologies with less manipulation of the DOM like React, or Vue.js, the library is nowhere near what it used to be.
โก๏ธ Bootstrap removed its jQuery dependencies in its 5th version, to make the library more lightweight.
โก๏ธ jQuery.terminal (2.8k โญ) | ๏ธ jQuery Migrate (1.9k โญ)
Snippets
- Scroll to the bottom of an element
const scrollToTheBottomOfElement = (e) => {
let previous = e.scrollHeight;
while (1) {
e.scrollTo(0, e.scrollHeight);
if (e.scrollHeight === previous) break;
previous = e.scrollHeight;
}
}
๐ป To-do ๐ป
Stuff that I found, but never read/used yet.
data-*
- blob URL
- Proxy
- SessionStorage
- ServiceWorkers (article)
- Get QueryParams
window.location.href
/window.location.replace(URL)
- partytown
- fingerprintjs
- nw.js
print()
debugger; // only resuming when closed
document.write(" Dont open Developer Tools. ");
window.confirm('Are you sure?')
xxx:
for (;;) {
break xxx;
}