Tari Ibaba is a software developer with years of experience building websites and apps. He has written extensively on a wide range of programming topics and has created dozens of apps and open-source libraries.
After creating the array, we call the map() method on it, with a callback function as an argument. This function will be called and return a result for each word in the array.
In the function, we get the word’s first character with charAt(), convert it to uppercase with toUpperCase(), and concatenate it with the rest of the string.
We use the Stringslice() method to get the remaining part of the string. Passing 1 to slice() makes it return the portion of the string from the second (index of 1) character to the end.
The “__dirname is not defined in ES module scope” error happens in JavaScript when we try to access the __dirname global variable in an ES module. The __dirname and __filename global variables are defined in CommonJS modules, but not in ES modules.
We can fix the “__dirname is not defined in ES module scope” error by using certain functions to create a custom __dirname variable that works just like the global variable, containing the full path of the file’s current working directly.
So we get the current module’s file URL and pass it to the fileURLToPath function from the url module, to convert it to a file path. fileURLToPath returns a fully-resolved, platform-specific Node.js file path.
With this, we now have our own __dirname and __filename variables.
Here’s the output from logging them from a file on my computer.
__filename contains the absolute path of the current module file.
__dirname contains the absolute path of the current module file’s directory.
Create utility for __dirname and __filename
If we access the __dirname and __filename variables frequently, we can abstract the logic for creating them in a utility module and avoid unnecessary repetition.
To use an image as a link in React, wrap the image in an anchor (a) tag. Clicking an image link will make the browser navigate to the specified URL.
For example:
App.js
import cbLogo from './cb-logo.png';
export default function App() {
return (
<div>
Click the logo to navigate to the site
<br />
<br />
<a href="https://wp.codingbeautydev.com" target="_blank" rel="noreferrer">
<img src={cbLogo} alt="Coding Beauty logo"></img>
</a>
</div>
);
}
We use an import statement to link the image into the file, and assign it to the src prop of the img element to display it.
The properties set on an a element will work as usual when it wraps an image. For instance, in the example, we set the a element’s target property to _blank, to open the URL in a new tab. Removing this will make it open in the same tab as normal.
We also set the rel prop to noreferrer for security purposes. It prevents the opened page from gaining access to any information about the page from which it was opened from.
Use image as React Router link
For React Router, you can use an image as link by wrapping the image in a Link element.
For example:
ImagePages.jsx
import { Link } from 'react-router-dom';
export default function ImagesPage() {
return (
<div>
<Link to="/nature" target="_blank" rel="noreferrer">
<img src="/photos/tree-1.png" alt="Nature"></img>
</Link>
</div>
);
}
With the useState hook, we create a state variable (message) to store the input field’s current value. We also create another state variable (updated) that will be updated with the input field value when the button is clicked.
We set an onChange event handler on the input field to execute an action. In the handler, we use the Event object’s target property to access the object representing the input element. The value property of this object contains the input value, so we pass it to setMessage to update message, and this reflects on the page.
After setting up the controlled input, we can now use message outside the handleChange handler to get the current value of the input field.
So in the onClick event handler we set on the button, we use setUpdated(message) to update the updated variable with the input field’s current value.
Get input value with ref
Alternatively, we can use a ref to get the value of an input field in React. After setting the ref on the input, we’ll be able to use the ref object to access the input field’s current value in the event handler.
We set an onKeyDown event listener on the input to perform an action when a key is pressed. In this listener, we use the KeyboardEvent object’s key property to check if the key pressed is Enter, and if so, we use the ref object to get the input’s value and update the updated variable with it.
While the data in a controlled input is handled by React state, the data in an uncontrolled input is handled by the DOM itself. This is why the input in the example above doesnโt have a value prop or onChange event handler set. Instead, we access the input field value with a React ref. The DOM updates this value when the text in the input is changed.
We create a ref object with the useRef hook and set it to the ref prop of the input. Doing this sets the current property of the ref object to the DOM object that represents the input element.
useRef returns a mutable ref object that does not change value when a component is updated. Also, modifying the value of this objectโs current property does not cause a re-render. This is in contrast to the setState update function returned from useState.
Although the React documentation recommends using controlled components, uncontrolled components offer some advantages. You might prefer them if the form is simple and doesnโt need instant validation, and values only need to be accessed on submission.
Create a boolean state variable to store the value of the checkbox.
Use v-model to set up a two-way binding between the checkbox’s value and the state variable.
If the checkbox is checked, the state variable will be true. Otherwise, it will be false.
For example:
App.vue
<template>
<div id="app">
<input
type="checkbox"
v-model="agreement"
name="agreement"
/>
<label for="agreement">
I agree to the terms and conditions
</label>
<br /><br />
<button :disabled="!agreement">Continue</button>
</div>
</template>
<script>
export default {
data() {
return {
agreement: false,
};
},
};
</script>
The checked property of the checkbox object indicates whether or not the checkbox is checked.
Every time the checkbox is checked or unchecked, the agreement state variable will be automatically updated to true or false respectively.
We set the button‘s disabled prop to the negation of agreement to disable and enable it when agreement is true and false respectively.
Check if checkbox is checked with ref
In most cases, v-model will be sufficient for checking if a checkbox if a checked in Vue. However, we can also use the ref attribute to get the input value. We can set this attribute on any DOM element and use the $refs property of the Vue instance to access the object that represents the element.
We set an onClick listener on the button. In this listener, we access the checkbox object using the ref, and use its checked property to determine the message that should be shown to the user when the button is clicked.
To push an object to an array in JavaScript, call the push() method on the array with the object as an argument, i.e., arr.push(obj). The push() method will add the element to the end of the array.
The push() method takes an object and adds it to the end of an array.
Push object to array during initialization
If the variable is newly created just before the object is pushed (like in the previous example), you can simply place the object in between the square brackets ([]) to include it in the array as the variable is initialized:
const obj = { name: 'Jeff' };
// ๐ Push object to array with initialization
const arr = [obj];
console.log(arr);
Push multiple objects to array
The push() method actually accepts a variable number of arguments. They are each added to the end of the array, in the order in which they are passed to push().
The push() method adds an object to the array in place, which means it gets modified. If you don’t want this, you can use the spread syntax (...) to create a copy of the original array, before calling push():
Similar to what we did earlier, we can include the object in the square brackets, after the spread syntax, to push the object to the array’s copy as it is initialized:
We use the document.querySelectorAll() method to select all DOM elements from which we want to remove the class.
We iterate over the elements in the list object with the forEach() method. This forEach() method works similarly to ArrayforEach().
document.getElementsByClassName() method
We can use the document.getElementsByClassName() method in place of the document.querySelectorAll() method when the selector is a class selector. For getElementsByClassName(), we pass the class name without the . (dot), and we use Array.from() to convert the result to an array before the iteration with forEach().
const elements = Array.from(document.getElementsByClassName('text'));
elements.forEach((element) => {
element.classList.remove('big');
});
classList.remove() method
We use the classList.remove() method to remove a class from the elements. You can remove multiple classes by passing more arguments to remove().
const elements = document.querySelectorAll('.text');
elements.forEach((element) => {
element.classList.remove('big', 'bold');
});
If any of the classes passed to remove() doesn’t exist on the element, remove() will ignore it, instead of throwing an error.
Remove class from multiple elements with different selectors
Sometimes there is no common selector between the elements that you want to remove the class from. For such a case, you can pass multiple comma-separated selectors to the querySelectorAll() method.
Just like the classList.remove() method removes one or more classes from an element, the classList.add() method adds one or more classes to an element. This means that we can use it in the forEach() method to remove a class from multiple DOM elements:
const elements = document.querySelectorAll('.text');
elements.forEach((element) => {
element.classList.add('italic', 'underline');
});
To sort an array of objects by a boolean property in JavaScript, call the sort() method on the array with a callback as an argument. In the function, use the boolean property to return an expression that evaluates to a positive number, a negative number, or zero. This will sort the array in place, by the boolean property, i.e.:
const trueFirst = users.sort((a, b) => b.boolProp - a.boolProp);
const falseFirst = users.sort((a, b) => a.boolProp - b.boolProp);
The Arraysort() method takes a callback function as an argument. It uses the value returned from this callback to determine the order in which to sort the values. For each item pair in the array, a and b:
If the returned value is negative, a is placed before b in the sorted array.
If the value is positive, b is placed before a.
If the value is 0, the order of a and b is not changed.
const arr = [5, 3, 8, 1, 7];
// Sort in ascending order
arr.sort((a, b) => a - b);
console.log(arr); // [ 1, 3, 5, 7, 8 ]
// Sort in descending order
const desc = arr.sort((a, b) => b - a);
console.log(desc); // [ 8, 7, 5, 3, 1 ]
Because we used the subtraction operator (-) on the boolean values, they are coerced to numbers before the subtraction happens. Truthy values are coerced to 1, and falsy values 0.
After seeing how the sort() method and boolean coercion works, it’s easy to understand the way the was sorted to place the objects with a boolean property value of true above the ones with false, and vice versa.
Sort object array by boolean property without mutation
The sort() method sorts the array in place, which means it gets modified. To prevent this, we can use the spread syntax (...) to create a shallow copy of the array for the sort:
In this article, we’re going to learn how to easily implement infinite scrolling on a webpage using JavaScript.
What is infinite scroll?
Infinite scroll is a web design technique where more content is loaded automatically when the user scrolls down to the end. It removes the need for pagination and can increase the time users spend on our site.
Finished infinite scroll project
Our case study for this article will be a small project that demonstrates essential concepts related to infinite scroll.
Here it is:
HTML structure
Before looking at the JavaScript functionality, let’s check out the HTML markup for the project’s webpage.
The #image-containerdiv will contain the grid of images.
The #load-triggerdiv is observed by an Intersection Observer; more images will be loaded when this div comes within a certain distance of the bottom of the viewport.
The #bottom-paneldiv will contain an indicator of the number of images that have been loaded.
Detect scroll to content end
The detectScroll() function uses the Intersection Observer API to detect when the #bottom-paneldiv comes within a certain range of the viewport’s bottom. We set a root margin of -30px, so this range is 30px upwards from the bottom.
JavaScript
const loadTrigger = document.getElementById('load-trigger');
// ...
const observer = detectScroll();
// ...
function detectScroll() {
const observer = new IntersectionObserver(
(entries) => {
for (const entry of entries) {
// ...
loadMoreImages();
// ...
}
},
// Set "rootMargin" because of #bottom-panel height
{ rootMargin: '-30px' }
);
// Start watching #load-trigger div
observer.observe(loadTrigger);
return observer;
}
The callback passed to and Intersection Observer fires after the observe() call, so the images are loaded as the page is loaded.
Display skeleton images
Before the actual images are loaded, we first show a blank skeleton image with a loading animation. We store the image elements in an array variable to update them when their respective images have been loaded.
JavaScript
const imageClass = 'image';
const skeletonImageClass = 'skeleton-image';
// ...
// This function would make requests to an image server
function loadMoreImages() {
const newImageElements = [];
// ...
for (let i = 0; i < amountToLoad; i++) {
const image = document.createElement('div');
// Indicate image load
image.classList.add(imageClass, skeletonImageClass);
// Include image in container
imageContainer.appendChild(image);
// Store in temp array to update with actual image when loaded
newImageElements.push(image);
}
// ...
}
To display each image, we create a div and add the image and skeleton-image classes to it. Here are the CSS definitions for these classes:
CSS
.image,
.skeleton-image {
height: 50vh;
border-radius: 5px;
border: 1px solid #c0c0c0;
/* Three per row, with space for margin */
width: calc((100% / 3) - 24px);
/* Initial color before loading animation */
background-color: #eaeaea;
/* Grid spacing */
margin: 8px;
/* Fit into grid */
display: inline-block;
}
.skeleton-image {
transition: all 200ms ease-in;
/* Contain ::after element with absolute positioning */
position: relative;
/* Prevent overflow from ::after element */
overflow: hidden;
}
.skeleton-image::after {
content: "";
/* Cover .skeleton-image div*/
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
/* Setup for slide-in animation */
transform: translateX(-100%);
/* Loader image */
background-image: linear-gradient(90deg, rgba(255, 255, 255, 0) 0, rgba(255, 255, 255, 0.2) 20%, rgba(255, 255, 255, 0.5) 60%, rgba(255, 255, 255, 0));
/* Continue animation until image load*/
animation: load 1s infinite;
}
@keyframes load {
/* Slide-in animation */
100% {
transform: translateX(100%)
}
}
Update skeleton images
Instead of getting images from a server, we get colors. After all the colors are loaded, we loop through the skeleton images, and for each image, we remove the skeleton-image class and apply the color.
JavaScript
function loadMoreImages() {
// ...
// Create skeleton images and stored them in "newImageElements" variable
// Simulate delay from network request
setTimeout(() => {
// Colors instead of images
const colors = getColors(amountToLoad);
for (let i = 0; i < colors.length; i++) {
const color = colors[i];
newImageElements[i].classList.remove(skeletonImageClass);
newImageElements[i].style.backgroundColor = color;
}
}, 2000);
// ...
}
The getRandomColor() function takes a number and returns an array with that number of random colors.
JavaScript
function getColors(count) {
const result = [];
let randUrl = undefined;
while (result.length < count) {
// Prevent duplicate images
while (!randUrl || result.includes(randUrl)) {
randUrl = getRandomColor();
}
result.push(randUrl);
}
return result;
}
getColors() uses a getRandomColor() function that returns a random color, as its name says.
JavaScript
function getRandomColor() {
const h = Math.floor(Math.random() * 360);
return `hsl(${h}deg, 90%, 85%)`;
}
Stop infinite scroll
To save resources, we stop observing the load trigger element after all possible content has been loaded.
Let’s say we have 50 images that can be loaded and the loadLimit is 9. When the first batch of images is loaded, the amountToLoad should be 9, with 9 displayed images. When the fifth batch is to be loaded, the amountToLoad should still be 9, with 45 displayed images.
On the sixth batch, there’ll only be 5 images left to load, so the amountToLoad should now be 5, taking the displayed images to 50. This sixth batch of images will be the final one to be loaded, after which we’ll stop watching the load trigger element, with a call to the unobserve() method of the Intersection Observer.
So we use the Math.min() method to ensure that the amountToLoad is always correct. amountToLoad should never be more than the, and never less than the images left to load.
If the user scrolls down rapidly, it’s likely that the Intersection Observer will fire multiple times, causing multiple image loads in a short period of time, creating performance problems.
To prevent this, we can use a timer to limit the number of times multiple image batches can be loaded within a certain time period. This is called throttling.
The throttle() function accepts a callback and a time period in milliseconds (time). It will not invoke the callback if the current throttle() call was made withing time ms of the last throttle() call.
JavaScript
let throttleTimer;
// Only one image batch can be loaded within a second
const throttleTime = 1000;
// ...
function throttle(callback, time) {
// Prevent additional calls until timeout elapses
if (throttleTimer) {
console.log('throttling');
return;
}
throttleTimer = true;
setTimeout(() => {
callback();
// Allow additional calls after timeout elapses
throttleTimer = false;
}, time);
}
By calling throttle() in the Intersection Observer’s callback with a time of 1000, we ensure that loadMoreImages() is never called multiple times within a second.
You can check out the complete source code for this project in CodePen. Here’s an embed:
Conclusion
In the article, we learned the basic elements need to implement infinite scroll functionality using JavaScript. With the Intersection Observer API, we observe a load trigger element and load more content when the element gets within a certain distance of the viewport’s bottom. With these ideas in mind, you should able to easily add infinite scroll to your project, customized according to your unique needs.
Let’s go through these terms in case you’re not familiar with them
XML (eXtensible Markup Language) is a markup language similar to HTML that was designed to store and transport data. Unlike HTML, XML doesn’t have any predefined tags. Instead, we define our own tags according to our requirements.
JSON (JavaScript Object Notation) is a text format used to store and transport data based on JavaScript object syntax and is commonly used to build RESTful APIs.
We use this object to specify various options for customizing the conversion process.
In our example, we don’t pass an object, so the default options are used. We can pass an object with a spaces option to properly format and indent the resulting JSON.
import { xml2json } from 'xml-js';
const xml = `<?xml version="1.0" encoding="UTF-8"?>
<languages>
<language>
English
</language>
<language>
French
</language>
<language>
Spanish
</language>
</languages>`;
// ๐ Set "spaces" option
const json = xml2json(xml, { spaces: 2 });
console.log(json);