Categories
JavaScript Vue

Vue.js Basics — The Vue Instance

Vue.js is an easy to use web app framework that we can use to develop interactive front end apps.

In this article, we’ll look more closely at the Vue instance, including how to define it and some properties of it.

Characteristics of the Vue Instance

Each Vue.js app begins by defining a new Vue instance. The Vue constructor takes an options object that takes various properties.

We often use vm to refer to a Vue instance, where vm stands for ViewModel.

A Vue app roughly follows the Model-View-ViewModel pattern, where the ViewModel has the business logic of the app, View has the markup that users see, and Model has the data.

For example, we can define a Vue instance as follows:

const vm = new Vue({ });

Each Vue app consists of a root Vue instance and it’s created with new Vue . We can organize it in a tree for easier maintenance.

Data and Methods

The options object that pass into the Vue constructor can have data and methods.

For example, if we define a Vue instance as follows:

const vm = new Vue({  
  el: "#app",  
  data: { foo: "bar" }  
});

Then when we add:

console.log(vm.foo);

below our vm definition, we get 'bar' since data.foo has the value 'bar' .

In other words, if we have:

const data = { foo: "bar" };  
const vm = new Vue({  
  el: "#app",  
  data  
});  
console.log(vm.foo === data.foo);

Then the console.log will log true .

When the data changes, the app will re-render with the new data.

If we create a new property in vm and set it as follows:

let data = { foo: "bar" };  
const vm = new Vue({  
  el: "#app",  
  data  
});  
vm.a = 1;

The app won’t re-render. On the other hand, if we write:

let data = { foo: "bar", a: 1 };  
const vm = new Vue({  
  el: "#app",  
  data  
});

Then the app will re-render. This means that we have to put our data that we want to render in the data field.

If we freeze the object that we pass to data with Object.freeze() , then the Vue app won’t re-render since properties can’t be changed, so new changes can’t propagate since they aren’t set in the object.

So if we have:

let data = { foo: "bar", a: 1 };  
Object.freeze(data);  
const vm = new Vue({  
  el: "#app",  
  data  
});

No changes can be made after the initial render since we froze data with Object.freeze .

The Vue instance also exposes a number of instance properties and methods.

They’re prefixed with the $ so that we know they’re part of the Vue instance.

$el

We have the $el property to get the DOM element that the Vue instance resides in.

For example, if we have:

let data = { foo: "bar" };  
const vm = new Vue({  
  el: "#app",  
  data  
});

console.log(vm.$el === document.getElementById("app"));

Then the console.log will log true since our Vue instance resides in an element with ID app .

$data

The $data property will get us the value of the data property that we set in the options object that we passed into the Vue constructor.

So if we have:

let data = { foo: "bar" };  
const vm = new Vue({  
  el: "#app",  
  data  
});

console.log(vm.$data === data);

Then we get true from the console.log since data references the same object as vm.$data since we set it as the value of the data property of the options object that we passed into the Vue constructor.

$watch

$watch is an instance method that lets us watch for changes in the data object that we set as the value of the options object.

For example, if we have:

let data = { foo: "bar" };  
const vm = new Vue({  
  el: "#app",  
  data  
});

vm.$watch("foo", (newValue, oldValue) => {  
  console.log(newValue, oldValue);  
});

in src/index.js and:

<!DOCTYPE html>  
<html>  
  <head>  
    <title>Hello</title>  
    <meta charset="UTF-8" />  
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>  
  </head> <body>  
    <div id="app">  
      <input type="text" v-model="foo" />  
    </div>  
    <script src="./src/index.js"></script>  
  </body>  
</html>

in index.html . Then when we type in something to the input box, we should get some console.log output from:

vm.$watch("foo", (newValue, oldValue) => {  
  console.log(newValue, oldValue);  
});

This is because changes are constantly being watched in the data.foo property. v-model automatically updates the value for foo as we type into the box.

So as we’re typing, the changes to foo are being watched and it’s logged by the handler function that we passed into the $watch method.

Conclusion

The Vue instance is created by passing in an options object with the data , el , and methods properties to the Vue constructor. It returns an instance of our Vue app.

The instance has the $el property to get the DOM element that our app resides in.

Also, there’s the $data property to get the value of the data property to get the data that we set when we pass it in as the value of the data property of the options object.

Finally, we have the $watch method to watch for changes in our data.

Categories
JavaScript Testing

Running Repetitive Test Code with Jest

In our unit tests, we often have to write test code that runs before and after each test. We’ve to write them often to run code to set up fixtures before tests and run code to clean up everything after tests.

We can easily do this with Jest since it comes with a few hooks to do this.

In this article, we’ll look at how to write repetitive setup and teardown code in a way that isn’t repetitive.

How to Write Setup and Teardown Code

With Jest, we can write setup and teardown code by using the beforeEach and afterEach hooks.

beforeEach runs code before each test, and afterEach runs code after each test. The order applies inside a describe block and if there’s no describe block, for the whole file.

For example, given that we have the following code in example.js :

const getStorage = (key) => localStorage.getItem(key);  
const setStorage = (key, value) => localStorage.setItem(key, value);  
module.exports = {  
    getStorage,  
    setStorage  
}

We write the test for them as follows:

const { getStorage, setStorage } = require('./example');

beforeEach(() => {  
    localStorage.setItem('foo', 1);  
    expect(localStorage.getItem('bar')).toBeNull();  
});

afterEach(() => {  
    localStorage.clear();  
});

test('getStorage("foo") is 1', () => {  
    expect(getStorage('foo')).toBe('1');  
});

test('setStorage saves data to local storage', () => {  
    setStorage('bar', 2);  
    const bar = +localStorage.getItem('bar');  
    expect(getStorage('foo')).toBe('1');  
    expect(bar).toBe(2);  
});

We pass a callback function into the beforeEach hook to run code before each test that we have below.

In this example, we prepopulate local storage with an item with key 'foo' and value '1' .

We also check that local storage doesn’t have the item with key 'bar’ with:

expect(localStorage.getItem('bar')).toBeNull();

All tests will pass with the expect we have above since we’ll run localStorage.clear() in the afterEach hook.

Likewise, we pass in a callback to afterEach to run code after each test is run.

We clear local storage with:

localStorage.clear();

Then when running the tests, we get that item with key'bar' is null since we didn’t populate it in the first test.

In the second test, we’ll get that expect(getStorage(‘foo’)).toBe(‘1’); passing since we populated the local storage with it in our beforeEach hook.

Then since we ran:

setStorage('bar', 2);

to to save an item with key 'bar' and value '2' , we’ll get that:

expect(bar).toBe(2);

passing since we saved the item in the test.

Asynchronous Code

In the example above, we ran synchronous code in our hooks. We can also run asynchronous code in our hooks if they either take a done parameter or return a promise.

If we want to run a function that takes a callback and runs it asynchronously as follows:

const asyncFn = (callback) => {  
    setTimeout(callback, 500);  
}

Then in the beforeEach hook we can run asyncFn as follows:

const { asyncFn } = require('./example');

beforeEach((done) => {  
    const callback = () => {  
        localStorage.setItem('foo', 1);  
        done();  
    }  
    asyncFn(callback);  
    expect(localStorage.getItem('bar')).toBeNull();  
});

It does the same thing as the previous beforeEach callback except it’s done asynchronously. Note that we call done passed in from the parameter in our callback.

If we omit, it, the tests will fail as they time out. The code in the tests is the same as before.

We can wait for promises to resolve by returning it in the callback we pass into the hooks.

For example, in example.js , we can write the following function to run a promise:

const promiseFn = () => {  
    return new Promise((resolve) => {  
        localStorage.setItem('foo', 1);  
        resolve();  
    });  
}

Then we can put promiseFn in module.exports and then run it in our beforeEach by running:

beforeEach(() => {  
    expect(localStorage.getItem('bar')).toBeNull();  
    return promiseFn();  
});

We ran promiseFn which we imported before this hook and we return the promise returned by that function, which sets the local storage like the first example except it’s done asynchronously.

Then after we put everything together, we have the following code in example.js , which we run in our test code in the hooks and for testing:

const getStorage = (key) => localStorage.getItem(key);  
const setStorage = (key, value) => localStorage.setItem(key, value);  
const asyncFn = (callback) => {  
    setTimeout(callback, 500);  
}  
const promiseFn = () => {  
    return new Promise((resolve) => {  
        localStorage.setItem('foo', 1);  
        resolve();  
    });  
}  
module.exports = {  
    getStorage,  
    setStorage,  
    asyncFn,  
    promiseFn  
}

Then we have asyncExample.test.js to change the hook to be asynchronous:

const { getStorage, setStorage, asyncFn } = require('./example');

beforeEach((done) => {  
    const callback = () => {  
        localStorage.setItem('foo', 1);  
        done();  
    }  
    asyncFn(callback);  
    expect(localStorage.getItem('bar')).toBeNull();  
});

afterEach(() => {  
    localStorage.clear();  
});

test('getStorage("foo") is 1', () => {  
    expect(getStorage('foo')).toBe('1');  
});

test('setStorage saves data to local storage', () => {  
    setStorage('bar', 2);  
    const bar = +localStorage.getItem('bar');  
    expect(getStorage('foo')).toBe('1');  
    expect(bar).toBe(2);  
});

Then in example.test.js we have:

const { getStorage, setStorage } = require('./example');

beforeEach(() => {  
    localStorage.setItem('foo', 1);  
    expect(localStorage.getItem('bar')).toBeNull();  
});

afterEach(() => {  
    localStorage.clear();  
});

test('getStorage("foo") is 1', () => {  
    expect(getStorage('foo')).toBe('1');  
});

test('setStorage saves data to local storage', () => {  
    setStorage('bar', 2);  
    const bar = +localStorage.getItem('bar');  
    expect(getStorage('foo')).toBe('1');  
    expect(bar).toBe(2);  
});

and finally in promiseExample.test.js we have:

const { getStorage, setStorage, promiseFn } = require('./example');

beforeEach(() => {  
    expect(localStorage.getItem('bar')).toBeNull();  
    return promiseFn();  
});

afterEach(() => {  
    localStorage.clear();  
});

test('getStorage("foo") is 1', () => {  
    expect(getStorage('foo')).toBe('1');  
});

test('setStorage saves data to local storage', () => {  
    setStorage('bar', 2);  
    const bar = +localStorage.getItem('bar');  
    expect(getStorage('foo')).toBe('1');  
    expect(bar).toBe(2);  
});

BeforeAll and AfterAll

To only run the setup and teardown code once in each file, we can use the beforeAll and afterAll hooks. We can pass in a callback with the code that we want to run like we did with the beforeEach and afterEach hooks.

The only difference is that the callbacks are run once before the test in a file is run and after the test in a file is run instead of running them before and after each test.

Callback of beforeAll runs after the callback for beforeEach and callback for afterAll runs after the callback forafterEach .

The order applies inside a describe block and if there’s no describe block, for the whole file.

Running Only One Test

We can write test.only instead of test to run only one test. This is handy for troubleshooting since we don’t have to run all the tests and it helps us pin issues with the test that’s failing.

To write test code that needs to be run for all tests, we use the beforeEach and afterEach hooks in Jest.

To write test code that’s only run per describe block or file, we can use the beforeAll and afterAll hooks.

Callback of beforeAll runs after the callback for beforeEach and callback for afterAll runs after the callback forafterEach .

Categories
JavaScript

More Things we can do with JavaScript Regular Expressions

Regular expressions let us manipulate strings with ease. They are patterns that let us match the text in pretty much in any way we imagine. Without it, we would have trouble searching for text with complex patterns. It’s also useful to check inputs against regular expressions for form validation. In this article, we’ll look at advanced special characters that are part of regular expressions and also JavaScript regular expression methods.

We can define JavaScript regular expressions with the literal as follows:

const re = /a/

Or, we can use the regular expression constructor to define the regular expression object by passing in a string to the RegExp constructor as follows:

const re = new RegExp('a');

More Special Characters

There’re more special characters that we can use to form regular expressions:

[^xyz]

Matches whatever character isn’t in the brackets and shows up up first. For example, if we have the string xylophone and the pattern [^xyz] , then it matches l since it’s the first character that’s not x , y or x in xylohphone .

[\b]

This pattern matches a backspace character.

\b

This pattern matches a word boundary. A word boundary is the position of a string that’s between a word character followed by a non-word character.

For example, if we have the pattern \babc and the string abc , then we get abc as the match since we have \b at the beginning of the string.

If we have abc\b , then we get abc as the match for abc 1 since \b matches the boundary at the end of the string as we have space after abc .

abc\babc won’t match anything because we have word characters before and after the \b so there’s no word boundary,

\B

Matches a non-word boundary. It matches anything before the first character of the string, after the last character of a string, between 2-word characters, between 2 non-word characters or an empty string.

For example, if we have the pattern ab\B. and the string abc 1 , then abc will be the match.

\cX

This pattern matches a control character of a string, where X is A to Z.

\d

Matches any digit. It’s the same as [0-9] For example, \d matches 1 in 123.

\D

This pattern matches any non-digit characters. It’s the same as [^0-9] . For example, \D matches a in the string abc123 .

\f

Matches the form feed character.

\n

Matches the line feed character.

\r

Matches the carriage return character.

\s

Matches a white space character.

\S

Matches any non-whitespace character.

\t

Matches the tab character.

\v

Matches the vertical tab character.

\W

Matches any non-word character. It’s the same as [^A-Za-z0-9_] . For example, if we have the string /. , then we get the / as the match.

\n

If n is a positive integer, then it’s a backreference to the last substring that matches the n capturing group.

For example, if we have a regular expression a(_)b\1 and the string a_b_c , then we get the matches a_b_ and _ since we have the _ in both substrings.

\0

Matches the null character.

\xhh

Matches the character with the code hh where hh is 2 decimal digits. For example, since the hexadecimal code for © is A9 , we can match it with the pattern \xA9 .

\uhhhh

Matches the character with the code hhhh , where hhhh is 4 hexadecimal digits.

\u{hhhh}

Matches the character with the Unicode value hhhh , where hhhh is 4 hexadecimal digits.

Regular Expression Methods

The JavaScript regular expression object has a few methods that let us do various things with regular expressions like searching strings, testing if strings match a pattern, replace strings and so on.

The methods are below.

exec

The exec method searches for a match of the regular expression in a string. It returns the results array or null if there’re no matches.

For example, if we write:

/[a-z0-9.]+@[a-z0-9.]+.[a-z]/.exec('[abc@abc.com](mailto:abc@abc.com)')

Then we get back:

["[abc@abc.com](mailto:abc@abc.com)", index: 0, input: "[abc@abc.com](mailto:abc@abc.com)", groups: undefined]

We can also use the g flag to search for the whole string for the match. For example, we can write:

/\d+/ig.exec('123')

Then we get 123 as the match.

test

The test method executes a search for a match between a regular expression and the specified string. It returns true if there’s a match and false otherwise.

For example, /foo/.test(‘foo’) returns true , but /foo/.test(‘bar’) returns false .

match

The match method returns all matches of a string against a regular expression.

For example, if we write:

'table tennis'.match(/[abc]/g)

Then we get back:

["a", "b"]

since we have the g flag to search the whole string for matches. If we remove the g flag, then we get the first match only. For instance, if we write:

'table tennis'.match(/[abc]/)

Then we get back:

["a", index: 1, input: "table tennis", groups: undefined]

**matchAll**

The matchAll method returns all matches of a string against a regular expression in a iterator which lets us get the results with the spread operator or a for...of loop.

For example, if we write:

[...'table tennis'.matchAll(/[abc]/g)]

Then we get back:

0: ["a", index: 1, input: "table tennis", groups: undefined]  
1: ["b", index: 2, input: "table tennis", groups: undefined]

since we have the g flag to search the whole string for matches. If we remove the g flag, then we get the first match only. For instance, if we write:

[...'table tennis'.matchAll(/[abc]/)]

Then we get back:

0: ["a", index: 1, input: "table tennis", groups: undefined]

search

The search method gets the index of the match of the string. It returns -1 is no match is found. For example, we can write:

'table tennis'.search(/[abc]/)

Then we get back 1 since we have a as the second character of 'table tennis' .

However, if we have:

'table tennis'.search(/[xyz]/)

Then we get back -1 because all 3 letters don’t exist in 'table tennis' .

replace

The replace method finds matches in a string and then replace the matches with the string that we specify.

For example, if we have:

'table tennis'.replace(/[abc]/, 'z')

Then we get:

"tzble tennis"

split

We can use the split method to split a string according to the pattern that we put in as the delimiter. It returns an array of strings that are split from the original string according to the matches of the regular expression.

For example, if we have:

'a 1 b 22 c 3'.split(/\d+/)

Then we get back:

["a ", " b ", " c ", ""]

As we can see, there’re lots of characters and combinations of them that we can search for with JavaScript regular expressions. It lets us do string validation and manipulation very easy since we don’t have to split them and check them. All we have to do is to search for them with regular expression objects.

Likewise, splitting and replace strings by complex patterns is also made easy with regular expression objects and their split and replace methods respectively.

Categories
Express JavaScript

Guide to the Express Response Object — Redirection and Templates

The Express response object lets us send a response to the client.

Various kinds of responses like strings, JSON, and files can be sent. Also, we can send various headers and status code to the client in addition to the body.

In this article, we’ll look at various properties of the response object, including sending the Links response header, redirect to different paths and URLs, and rendering HTML.

Methods

res.links(links)

The links method joins an object with links together and sends the Link response header with the links joined together.

For example, we can use it as follows:

const express = require('express');  
const bodyParser = require('body-parser');
const app = express();app.use(bodyParser.json());  
app.use(bodyParser.urlencoded({ extended: true }));
app.get('/', (req, res) => {  
  res  
    .links({  
      next: 'http://foo.com?page=1',  
      last: 'http://foo.com?page=2'
    })  
    .send();  
});
app.listen(3000, () => console.log('server started'));

Then we get that the Link response header has the value:

<http://foo.com?page=1>; rel="next", <http://foo.com?page=2>; rel="last"

when we make a request to the / path.

res.location(path)

The location method sends the Location response header with the specified path parameter as the value.

For example, we can use it as follows:

const express = require('express');  
const bodyParser = require('body-parser');
const app = express();app.use(bodyParser.json());  
app.use(bodyParser.urlencoded({ extended: true }));
app.get('/', (req, res) => {  
  res  
    .location('http://medium.com')  
    .send();  
});
app.listen(3000, () => console.log('server started'));

Then we get:

http://medium.com

as the value of the Location response header when we make a request to the / route.

res.redirect([status,] path)

The redirect method redirects to the URL or path specified with the specified status . If no status is specified, the default is 302.

For example, we can use it as follows to redirect to another route:

const express = require('express');  
const bodyParser = require('body-parser');
const app = express();app.use(bodyParser.json());  
app.use(bodyParser.urlencoded({ extended: true }));
app.get('/', (req, res) => {  
  res.redirect('/bar');  
});

app.get('/bar', (req, res) => {  
  res.send('bar');  
});

app.listen(3000, () => console.log('server started'));

Then we should see bar since we redirected to the bar route.

We can specify a status code as follows:

const express = require('express');  
const bodyParser = require('body-parser');
const app = express();app.use(bodyParser.json());  
app.use(bodyParser.urlencoded({ extended: true }));
app.get('/', (req, res) => {  
  res.redirect(301, '/bar');  
});

app.get('/bar', (req, res) => {  
  res.send('bar');  
});

app.listen(3000, () => console.log('server started'));

We can also redirect to a full URL:

const express = require('express');  
const bodyParser = require('body-parser');
const app = express();
app.use(bodyParser.json());  
app.use(bodyParser.urlencoded({ extended: true }));
app.get('/', (req, res) => {  
    res.redirect('http://google.com');  
});

app.listen(3000, () => console.log('server started'));

Then when we go to / in our browser, we get Google displayed in our browser.

We can redirect back to the referrer with:

res.redirect('back')

and we can also redirect to a higher level URL, which defaults to / .

We can use it as follows:

const express = require('express');  
const bodyParser = require('body-parser');
const app = express();  
const post = express.Router();app.use(bodyParser.json());  
app.use(bodyParser.urlencoded({ extended: true }));

post.get('/', (req, res) => {  
    res.redirect('..');  
});

app.get('/', (req, res) => {  
    res.send('hi');  
});

app.use('/post', post);app.listen(3000, () => console.log('server started'));

Then we get hi if we go to /post since we see redirect to '..' .

res.render(view [, locals] [, callback])

res.render lets us render HTML using a template. Once we set the HTML template engine, we can use template files to display the results.

The locals object has the properties that we can access in the templates.

And the callback can get the error and the rendered HTML string.

The view argument has the string with the template’s file name. The view’s folder is set before we can call res.render so we don’t need the full folder name.

For example, we can use the Pug template to render templates as follows:

const express = require('express')  
const app = express()  
app.use(express.json())  
app.use(express.urlencoded({ extended: true }))  
app.set('views', './views');  
app.set('view engine', 'pug');
app.get('/', (req, res, next) => {  
  res.render('index', { pageTitle: 'Hello', pageMessage: 'Hello there!' });  
})  
app.listen(3000, () => console.log('server started'));

Then we can add a template file called index.pug to the views folder as follows:

html  
  head  
    title= pageTitle  
  body  
    h1= pageMessage

Then we get:

https://thewebdev.info/wp-content/uploads/2020/04/hello-there.png

We can also pass in a callback to the render method as follows:

const express = require('express')  
const app = express()  
app.use(express.json())  
app.use(express.urlencoded({ extended: true }))  
app.set('views', './views');  
app.set('view engine', 'pug');app.get('/', (req, res, next) => {  
  res.render(  
    'index',  
    { pageTitle: 'Hello', pageMessage: 'Hello there!' },  
    (err, html) => {  
      if (err) {  
        next(err);  
      }  
      else {  
        console.log(html);  
      }  
    }  
  );  
})  
app.listen(3000);

Then we get the rendered HTML from the console.log :

<html><head><title>Hello</title></head><body><h1>Hello there!</h1></body></html>

Any error will be sent to the Express error handler with the next function.

Conclusion

We can render HTML results with render method.

With the redirect method, we can redirect to different paths and URLs with the different response code.

We can use the links and locations methods to send the Links and Locations response headers respectively.

Categories
JavaScript JavaScript Basics

More Useful DOM Traversal Methods

The main use of client-side JavaScript is to manipulate web pages dynamically. We can do this with the DOM traversal methods and properties available to DOM Node objects.

Adding or changing child and sibling elements is easy for any given element since there are properties built into DOM node objects to do so. The following are methods of a DOM node object for getting parent, child and sibling nodes or elements.

Below are more useful DOM traversal methods.

hasChildNodes

The hasChildNodes method returns a boolean that indicates whether the node it’s called on has any child nodes. Nodes include all kinds of nodes like element, text and comment nodes

For example, given the following HTML:

<div></div>

Then we can call hasChildNodes on it as follows:

const div = document.querySelector('div');  
console.log(div.hasChildNodes())

We should get false since the div has no child nodes.

On the other hand, if we have:

<div>  
  foo  
</div>

Then hasChildNodes returns true since there’s a text node inside the div.

insertBefore

The insertBefore method lets us add a node before the node that this method called on. It takes 2 arguments. The first is the new node to be inserted. The second is the node before the new node that it’s inserted, which is called the reference node.

If the reference node is null , then it’ll be inserted at the end of the list of child nodes.

It returns the add child except when the new node is a DocumentFragement . In that case, only DocumentFragement is returned.

For example, given the following HTML:

<div id='foo'>  
  foo  
  <div id='bar'>  
    bar  
  </div>  
</div>

We can add a new div element after the div with ID bar inside the one with ID foo by writing:

const foo = document.querySelector('#foo');
const newDiv = document.createElement('div');  
newDiv.textContent = 'new';
foo.insertBefore(newDiv, null);

The HTML structure looks like:

<div id="foo">  
  foo  
  <div id="bar">  
    bar  
  </div>  
  <div>new</div>  
</div>

after insertBefore is called.

If we pass in the second argument, then the element in the first argument will be inserted before the second one.

For example, given the following HTML:

<div id='foo'>  
  foo  
  <div id='bar'>  
    bar  
  </div>  
</div>

Then we can insert an element before the one with ID bar at the same level by writing:

const foo = document.querySelector('#foo');  
const bar = document.querySelector('#bar');
const newDiv = document.createElement('div');  
newDiv.textContent = 'new';
foo.insertBefore(newDiv, bar);

Then we get the following HTML after insertBefore is called:

<div id="foo">  
  foo  
  <div>new</div>  
  <div id="bar">  
    bar  
  </div>  
</div>

isEqualNode

The isEqualNode method checks whether 2 nodes are equal. They’re equal when they have the same type, defining characteristics like ID, the number of children, the same attributes, etc. The data points for comparison vary depending on the types of nodes.

It takes one argument, which is the node that we want to compare with.

For example, given the following HTML:

<div id='foo'>  
  foo  
</div>  
<div id='bar'>  
  bar  
</div>

Then we can write the following JavaScript to check if the div with ID foo and the div with ID bar are the same:

const foo = document.querySelector('#foo');  
const bar = document.querySelector('#bar');
console.log(foo.isEqualNode(bar));

The console.log should log false since they don’t have the same ID or content.

On the other hand, if we have the following HTML:

<div class='foo'>  
  foo  
</div>  
<div class='foo'>  
  foo  
</div>

Then we can compare them by writing:

const foo1 = document.querySelector('.foo:nth-child(1)');  
const foo2 = document.querySelector('.foo:nth-child(2)');
console.log(foo1.isEqualNode(foo2));

and the console.log should log true since they have identical class value and content.

isSameNode

The isSameNode method checks whether 2 Node objects reference the same node.

It takes one argument, which is the other node to test and returns a boolean indicating whether they’re the same.

For example, given the following HTML:

<div>  
  foo  
</div>

We can call isSameNode by writing:

const div = document.querySelector('div');console.log(div.isSameNode(div));

On the other hand, if we have:

<div>  
  foo  
</div>  
<div>  
  foo  
</div>

Then the following call to isSameNode will return false :

const div1 = document.querySelector('div:nth-child(1)');  
const div2 = document.querySelector('div:nth-child(2)');console.log(div1.isSameNode(div2));

This is because even though they look the same, they don’t reference the same node.

normalize

The normalize method cleans up the sub-tree of a Node by removing adjacent text nodes.

For example, if we have the following HTML:

<div>  
  foo  
</div>

Then before call normalize, we get that there’re 3 child nodes inside the div element after add 2 more text nodes to it as child nodes of the div. After we called it, it’s reducing the number of child nodes to 1.

We call as in the following code:

const div = document.querySelector('div');  
div.appendChild(document.createTextNode("foo "));  
div.appendChild(document.createTextNode("bar"));console.log(div.childNodes.length);  
div.normalize();  
console.log(div.childNodes.length);

The first console.log should log 3 and the second one should log 1.

removeChild

To remove the child node from a given node, we can use the removeChild method to remove the child from the DOM tree.

It takes a child element of the given node as an argument and then returns the node that was removed from the DOM tree.

For example, given that we have the following HTML:

<div id='foo'>  
  foo  
  <div id='bar'>  
    bar  
  </div>  
</div>

We can remove the div with ID bar by writing:

const foo = document.querySelector('#foo');  
const bar = document.querySelector('#bar');foo.removeChild(bar);

Then we should no longer see ‘bar’ in the browser window.

It only works with child nodes. For example, if we have:

<div id='foo'>  
  foo  
</div>  
<div id='bar'>  
  bar  
</div>

and run the same code, then we’ll get the error ‘Uncaught DOMException: Failed to execute ‘removeChild’ on ‘Node’: The node to be removed is not a child of this node.’

It’ll also throw an error if the node doesn’t exist.

replaceChild

The replaceChild method replaces the current one with the second one given in the parameter.

For example, if we have the following HTML:

<div id='foo'>  
  foo  
  <div id='bar'>  
    bar  
  </div>  
</div>

Then we can create a new element to replace the div with ID bar:

const foo = document.querySelector('#foo');  
const bar = document.querySelector('#bar');const baz = document.createElement('div');  
baz.textContent = 'baz';  
foo.appendChild(baz);foo.replaceChild(baz, bar);

The first argument has the new element and the second argument has the original one.

It also works for using elements that already exist to replace another one that exists.

For instance, if we have the following HTML:

<div id='foo'>  
  foo  
  <div id='bar'>  
    bar  
  </div>  
  <div id='baz'>  
    baz  
  </div>  
</div>

Then we can write:

const foo = document.querySelector('#foo');  
const bar = document.querySelector('#bar');  
const baz = document.querySelector('#baz');
foo.replaceChild(baz, bar);

to replace the div with ID bar with the one with ID baz.

The DOM traversal and manipulation methods are very useful. We can use them to check if they have the same content or referencing the same element. Also, there’re methods to replace nodes and clean up extraneous text nodes. There are also handy methods for inserting nodes and removing nodes.