Uncategorised

How to Convert JSON to XML in Node.js

We can use the xml-js library to easily convert a JSON string to an XML string in Node.js.

import { json2xml } from 'xml-js';

const jsonObj = {
  name: 'Garage',
  cars: [
    { color: 'red', maxSpeed: 120, age: 2 },
    { color: 'blue', maxSpeed: 100, age: 3 },
    { color: 'green', maxSpeed: 130, age: 2 },
  ],
};

const json = JSON.stringify(jsonObj);

const xml = json2xml(json, { compact: true, spaces: 4 });

console.log(xml);

This code will have the following output:

<name>Garage</name>
<cars>
    <color>red</color>
    <maxSpeed>120</maxSpeed>
    <age>2</age>
</cars>
<cars>
    <color>blue</color>
    <maxSpeed>100</maxSpeed>
    <age>3</age>
</cars>
<cars>
    <color>green</color>
    <maxSpeed>130</maxSpeed>
    <age>2</age>
</cars>

Install xml-js

Before using xml-js, we’ll need to install it in our project. We can do this with the NPM CLI.

npm i xml-js

Or with the Yarn CLI:

yarn add xml-js

After installation, we’ll be able to import it into a JavaScript module, like this:

import { json2xml } from 'xml-js';

We use import destructuring to access the json2xml() method directly from the library.

For a CommonJS module, we’ll import it like this instead:

const { json2xml } = require('xml-js');

The json2xml() function

The json2xml() function from the library has two parameters. The first is the JSON string to convert to XML, and the second is an object.

const xml = json2xml(json, { compact: true, spaces: 4 });

Customize conversion of JSON to XML in Node.js

This object is used to specify various options for customizing the conversion process.

In our example, we set the compact property to true to indicate that the JSON string input is in a compact form.

We set the spaces property to 4 to indent nested XML nodes by 4 spaces. So we can reduce the indentation by setting spaces to 1:

import { json2xml } from 'xml-js';

const jsonObj = {
  name: 'Garage',
  cars: [
    { color: 'red', maxSpeed: 120, age: 2 },
    { color: 'blue', maxSpeed: 100, age: 3 },
    { color: 'green', maxSpeed: 130, age: 2 },
  ],
};

const json = JSON.stringify(jsonObj);

const xml = json2xml(json, { compact: true, spaces: 1 });

console.log(xml);

Now we will have the following XML output:

<name>Garage</name>
<cars>
 <color>red</color>
 <maxSpeed>120</maxSpeed>
 <age>2</age>
</cars>
<cars>
 <color>blue</color>
 <maxSpeed>100</maxSpeed>
 <age>3</age>
</cars>
<cars>
 <color>green</color>
 <maxSpeed>130</maxSpeed>
 <age>2</age>
</cars>

Native conversion of JSON to XML in Node.js

If you don’t want to use any third-party libraries, then you can use this recursive function to convert JSON to XML in Node.js.

function JSONtoXML(obj) {
  let xml = '';
  for (let prop in obj) {
    xml += obj[prop] instanceof Array ? '' : '<' + prop + '>';
    if (obj[prop] instanceof Array) {
      for (let array in obj[prop]) {
        xml += '\n<' + prop + '>\n';
        xml += JSONtoXML(new Object(obj[prop][array]));
        xml += '</' + prop + '>';
      }
    } else if (typeof obj[prop] == 'object') {
      xml += JSONtoXML(new Object(obj[prop]));
    } else {
      xml += obj[prop];
    }
    xml += obj[prop] instanceof Array ? '' : '</' + prop + '>\n';
  }
  xml = xml.replace(/<\/?[0-9]{1,}>/g, '');
  return xml;
}

const jsonObj = {
  name: 'Garage',
  cars: [
    { color: 'red', maxSpeed: 120, age: 2 },
    { color: 'blue', maxSpeed: 100, age: 3 },
    { color: 'green', maxSpeed: 130, age: 2 },
  ],
};

const xml = JSONtoXML(jsonObj);

console.log(xml);

This code will produce the following output:

<name>Garage</name>

<cars>
<color>red</color>
<maxSpeed>120</maxSpeed>
<age>2</age>
</cars>
<cars>
<color>blue</color>
<maxSpeed>100</maxSpeed>
<age>3</age>
</cars>
<cars>
<color>green</color>
<maxSpeed>130</maxSpeed>
<age>2</age>
</cars>

How to Change the Style of an Element on Click in Vue

To change the style of an element on click in Vue:

  1. Create a boolean state variable to conditionally set the style on the element depending on the value of this variable.
  2. Set a click event handler on the element that toggles the value of the state variable.

For example:

App.vue

<template>
  <div id="app">
    <p>Click the button to change its color.</p>

    <button
      role="link"
      @click="handleClick"
      class="btn"
      :style="{
        backgroundColor: active ? 'white' : 'blue',
        color: active ? 'black' : 'white',
      }"
    >
      Click
    </button>
  </div>
</template>

<script>
export default {
  data() {
    return {
      active: false,
    };
  },
  methods: {
    handleClick() {
      this.active = !this.active;
    },
  },
};
</script>

<style>
.btn {
  border: 1px solid gray;
  padding: 8px 16px;
  border-radius: 5px;
  font-family: 'Segoe UI';
  font-weight: bold;
}
</style>
Clicking the element changes its stye.
Clicking the element changes its style.

The active state variable determines the style that will be applied to the element. When it is false (the default), a certain style is applied.

We set a click event handler on the element, so that the handler will get called when it is clicked. The first time this handler is called, the active variable gets toggled to true, which changes the style of the element.

Note

To prevent the style from changing every time the element is clicked, we can set the state variable to true, instead of toggling it:

handleClick() {
  this.active = true

  // this.active = !this.active
},

We used ternary operators to conditionally set the backgroundColor and color style on the element.

The ternary operator works like an if/else statement. It returns the value before the ? if it is truthy. Otherwise, it returns the value to the left of the :.

const treshold = 10;

const num = 11;

const result = num > treshold ? 'Greater' : 'Lesser';

console.log(result) // Greater

So if the active variable is true, the backgroundColor and color are set to white and black respectively. Otherwise, they’re set to blue and white respectively.

Change element style on click with classes

To change the style of an element on click in Vue, we can also create classes containing the alternate styles and conditionally set them to the class prop of the element, depending on the value of the boolean state variable.

For example:

App.vue

<template>
  <div id="app">
    <p>Click the button to change its color.</p>

    <button
      role="link"
      @click="handleClick"
      class="btn"
      :class="active ? 'active' : 'non-active'"
    >
      Click
    </button>
  </div>
</template>

<script>
export default {
  data() {
    return {
      active: false,
    };
  },
  methods: {
    handleClick() {
      this.active = !this.active;
    },
  },
};
</script>

<style>
.btn {
  border: 1px solid gray;
  padding: 8px 16px;
  border-radius: 5px;
  font-family: 'Segoe UI';
  font-weight: bold;
}

.active {
  background-color: white;
  color: black;
}

.non-active {
  background-color: blue;
  color: white;
}
</style>

We create two classes (active and non-active) with different styles, then we use the ternary operator to add the active class if the active variable is true, and add the non-active class if otherwise.

The advantage of using classes is that we get to cleanly separate the styles from the template markup. Also, we only need to use one ternary operator.

Change element style on click with event.currentTarget.classList

There are other ways to change the style of an element in Vue without using a state variable.

With classes defined, we can use the currentTarget.classList property of the Event object passed to the click event handler to change the style of the element.

For example:

App.vue

<template>
  <div id="app">
    <p>Click the button to change its color.</p>

    <button
      role="link"
      @click="handleClick"
      class="btn non-active"
    >
      Click
    </button>
  </div>
</template>

<script>
export default {

  methods: {
    handleClick(event) {
      // 👇 Change style
      event.currentTarget.classList.remove('non-active');
      event.currentTarget.classList.add('active');
    },
  },
};
</script>

<style>
.btn {
  border: 1px solid gray;
  padding: 8px 16px;
  font-family: Arial;
  font-size: 1.1em;
  box-shadow: 0 2px 5px #c0c0c0;
}

.active {
  background-color: white;
  color: black;
}

.non-active {
  background-color: rebeccapurple;
  color: white;
}
</style>
Clicking the element changes its style.
Clicking the element changes its style.

We don’t use a state variable this time, so we add the non-active class to the element, to customize its default appearance.

The click event listener receives an Event object used to access information and perform actions related to the click event.

The currentTarget property of this object returns the element that was clicked and had the event listener attached.

We call the classList.remove() method on the element to remove the non-active class, then we call the classList.add() method on it to add the active class.

Notice that the style of the element didn’t change anymore after being clicked once. If you want to toggle the style whenever it is clicked, you can use the toggle() class of the element to alternate the classes on the element.

App.vue

<template>
  <div id="app">
    <p>Click the button to change its color.</p>

    <button
      role="link"
      @click="handleClick"
      class="btn non-active"
    >
      Click
    </button>
  </div>
</template>

<script>
export default {
  methods: {
    handleClick(event) {

      // 👇 Alternate classes
      event.currentTarget.classList.toggle('non-active');
      event.currentTarget.classList.toggle('active');
    },
  },
};
</script>

<style>
.btn {
  border: 1px solid gray;
  padding: 8px 16px;
  font-family: Arial;
  font-size: 1.1em;
  box-shadow: 0 2px 5px #c0c0c0;
}

.active {
  background-color: white;
  color: black;
}

.non-active {
  background-color: rebeccapurple;
  color: white;
}
</style>
Clicking the element toggles its color.
Clicking the element toggles its color.

The classList.toggle() method removes a class from an element if it is present. Otherwise, it adds the class to the element.

How to Fix the “Cannot read property ‘addEventListener’ of null” Error in JavaScript

The “cannot read property ‘addEventListener’ of null” error occurs in JavaScript when you try to call the addEventListener() method on an element that cannot be found in the DOM. This happens for two main reasons:

  1. Accessing the addEventListener() method on an element absent from the DOM.
  2. Inserting the script tag referencing the JavaScript file at a point above the declaration of the DOM element in the HTML.

We’ll learn how to handle the error for these two scenarios in this article.

The "cannot read property 'addEventListener' of null" error occurring in JavaScript.

Cause 1: Accessing addEventListener() on an element not present in the DOM

index.js

const btn = document.getElementById('does-not-exist');

console.log(btn); // null

// ❌ Cannot read property 'addEventListener' of null
btn.addEventListener('click', () => {
  alert('You clicked the button');
});

When a method like getElementById() or querySelector() method is used to search for an element that doesn’t exist in the DOM, it returns null. And attempting to call the addEventListener() method on a null value will cause the error.

Solve: Ensure correct selector

To fix the “cannot read property ‘addEventListener’ of null” error, make sure the selector used the access the element is properly defined. Ensure that there are no mistakes in the ID or class name, and the correct symbols are used.

Solve: Check for null

To fix the “cannot read property ‘addEventListener’ of null” error, check that the element is not null before calling the addEventListener() method on it.

We can do this with an if statement:

const btn = document.getElementById('does-not-exist');

console.log(btn); // null

// ✅ Check if element exists before calling addEventListener()
if (btn) {
  // Not called
  btn.addEventListener('click', () => {
    alert('You clicked the button');
  });
}

When a value is placed in between the brackets of an if statement, it is coerced to a Boolean before being evaluated, i.e., truthy values become true, and falsy values become false. null is a falsy value, so it is coerced to false and the code in the if statement block is never run.

Note: In JavaScript, there are six falsy values: undefined, null, NaN, 0, '' (empty string) and false. Every other value is truthy.

We can also use the optional chaining operator (?.) to check if the element is null.

const btn = document.getElementById('does-not-exist');

console.log(btn); // null

// ✅ Check if element exists before calling addEventListener()

// Not called
btn?.addEventListener('click', () => {
  alert('You clicked the button');
});

The optional chaining operator (?.) is null-safe way of accessing a property or calling a method of an object. If the object is nullish (null or undefined), the operator prevents the member access and returns undefined instead of throwing an error.

Cause 2: Inserting the script tag above the DOM element

Another common cause of this error is placing the <script> tag referencing the JavaScript file at a point above where the target element is declared.

For example, in this HTML markup:

index.html

<!DOCTYPE html>
<html lang="en">
  <head>
    <title>Coding Beauty Tutorial</title>
    <!-- ❌ Script is run before button is declared -->
    <script src="index.js"></script>
  </head>
  <body>
    <button id="btn">Sign up</button>
  </body>
</html>

the script tag is placed in the head tag, above where the button element is declared, so the index.js file will not be able to access the button element.

index.js

const btn = document.getElementById('btn');

console.log(btn); // null

// ❌ TypeError: Cannot read properties of null
btn.addEventListener('click', () => {
  alert('You clicked the button');
});

Solve: Move script tag to bottom of body

To fix the error in this case, move the script tag to the bottom of the body, after all the HTML elements have been declared.

index.html

<!DOCTYPE html>
<html lang="en">
  <head>
    <title>Coding Beauty Tutorial</title>
  </head>
  <body>
    <button id="btn">Sign up</button>

    <!-- ✅ Script is run after button is declared -->
    <script src="index.js"></script>
  </body>
</html>

Now the index.js script file will have access to the button element and all the other HTML elements, because they would have already been declared when the script is run.

index.js

const btn = document.getElementById('btn');

console.log(btn); // HTMLButtonElement object

// ✅ Works as expected
btn.addEventListener('click', () => {
  alert('You clicked the button');
});

Solve: Access element in DOMContentLoaded event listener

Another way to fix the “cannot read property ‘addEventListener’ of null” error in JavaScript is to add a DOMContentLoaded event listener to the document, and access the element in this listener. With this approach it won’t matter where we place the script in the HTML.

index.html

<!DOCTYPE html>
<html lang="en">
  <head>
    <title>Coding Beauty Tutorial</title>

    <!-- Script placed above element accessed -->
    <script src="index.js"></script>
  </head>
  <body>
    <button id="btn">Sign up</button>
  </body>
</html>

The DOMContentLoaded event is fired when the browser has fully loaded the HTML, and the DOM tree has been built, but external resources like images and stylesheets may not have loaded. So regardless of where we place the script, the code in the listener is only called after all the declared HTML elements have been added to the DOM.

index.js

document.addEventListener('DOMContentLoaded', () => {
  const btn = document.getElementById('btn');

  console.log(btn); // HTMLButtonElement object

  // ✅ Works as expected
  btn.addEventListener('click', () => {
    alert('You clicked the button');
  });
});

Conclusion

We can fix the “cannot read property addEventListener’ of null” error in JavaScript by ensuring that the correct selector is defined, adding a null check to the element before calling addEventListener(), moving the script tag to the bottom of the body, or accessing the element in a DOMContentLoaded event listener added to the document.

How to Subtract 6 Months From a Date in JavaScript

To subtract 6 months from a date:

  1. Call the getMonth() method on the date to get the months.
  2. Subtract 6 from the return value of getMonth().
  3. Pass the result of the subtraction to the setMonth() method.

For example:

function subtract6Months(date) {
  date.setMonth(date.getMonth() - 6);

  return date;
}

// November 14, 2022
const date = new Date('2022-11-14T00:00:00.000Z');

const newDate = subtract6Months(date);

// May 14, 2022
console.log(newDate); // 2022-05-14T00:00:00.000Z

Our subtract6Months() function takes a Date object and the number of months to subtract as arguments. It returns the same Date object with 6 months subtracted.

The Date getMonth() returns a zero-based number that represents the month of a particular date.

The Date setMonth() method sets the months of a date to a specified zero-based number.

Note: “Zero-based” here means that 0 is January, 1 is February, 2 is March, etc.

If the months subtracted would decrease the year of the date, setMonths() will automatically update the date information to reflect this.

// January 10, 2022
const date = new Date('2022-01-10T00:00:00.000Z');

date.setMonth(date.getMonth() - 6);

// July 10, 2021
console.log(date); // 2021-07-10T00:00:00.000Z

In this example, we subtract 6 months from a date in January 2022. This makes the year automatically get rolled back to 2021 by setMonth().

Avoid side-effects

The setMonth() mutates the Date object it is called on. This introduces a side effect into our subtract6Months() function. To avoid modifying the passed Date and create a pure function, make a copy of the Date and call setMonth() on this copy, instead of the original.

function subtract6Months(date) {
  // 👇 Make copy with "Date" constructor
  const dateCopy = new Date(date);

  dateCopy.setMonth(dateCopy.getMonth() - 6);

  return dateCopy;
}

// August 13, 2022
const date = new Date('2022-08-13T00:00:00.000Z');

const newDate = subtract6Months(date);

// February 13, 2022
console.log(newDate); // 2022-02-13T00:00:00.000Z

// 👇 Original not modified
console.log(date); // 2022-08-13T00:00:00.000Z

Tip: Functions that don’t modify external state (i.e., pure functions) tend to be more predictable and easier to reason about, as they always give the same output for a particular input. This makes it a good practice to limit the number of side effects in your code.

2. date-fns subMonths() function

Alternatively, we can use the subMonths() function from the date-fns NPM package to quickly subtract 6 months from a date. It works like our pure subtractMonths() function. subMonths() takes a Date object and the number of months to subtract as arguments. It returns a new Date object with the months subtracted.

import { subMonths } from 'date-fns';

// July 26, 2022
const date = new Date('2022-07-26T00:00:00.000Z');

const newDate = subMonths(date, 6);

// January 26, 2022
console.log(newDate); // 2022-01-26T00:00:00.000Z

// Original not modified
console.log(date); // 2022-07-26T00:00:00.000Z

Note that the subMonths() function returns a new Date object without modifying the one passed to it.

How to Add a Class Conditionally in Vue

To add a class conditionally to an element in Vue, set the class prop to a JavaScript object where for each property, the key is the class name, and the value is the boolean condition that must be true for the class to be set on the element.

<p
  v-bind:class="{
    'class-1': class1,
    'class-2': class2,
  }"
>
  Coding
</p>

Here’s a complete example:

App.vue

<template>
  <div id="app">
    <input
      type="checkbox"
      name="class-1"
      v-model="class1"
    />
    <label for="class-1">Class 1</label>

    <br />

    <input
      type="checkbox"
      name="class-2"
      v-model="class2"
    />
    <label for="class-2">Class 2</label>

    <!-- 👇 Add classes conditionally -->
    <p
      v-bind:class="{
        'class-1': class1,
        'class-2': class2,
      }"
    >
      Coding
    </p>
    <p
      v-bind:class="{
        'class-1': class1,
        'class-2': class2,
      }"
    >
      Beauty
    </p>
  </div>
</template>

<script>
export default {
  data() {
    return {
      class1: false,
      class2: false,
    };
  },
};
</script>

<style>
.class-1 {
  font-size: 2em;
  font-weight: bold;
}

.class-2 {
  color: blue;
  text-transform: uppercase;
}
</style>
Setting a class conditionally in Vue.
The classes are only applied when their respective checkboxes are checked.

The class-1 class will only present on the element if the class1 variable is true, and the class-2 class will only be present if the class2 variable is true. The values of these variables are determined by the current checked state of their respective checkboxes since we use v-model to set up a two-way binding between the variables and the checkboxes.

Use :class shorthand

We can use :class as a shorthand for v-bind:class.

<p
  :class="{
    'class-1': class1,
    'class-2': class2,
  }"
>
  Coding
</p>
<p
  :class="{
    'class-1': class1,
    'class-2': class2,
  }"
>
  Beauty
</p>

Pass object as computed property

The JavaScript object passed doesn’t have to be inline. It can be stored as a computed property in the Vue component instance.

<template>
  <div id="app">
    ...
    <!-- 👇 Add classes conditionally -->
    <p :class="classObject">Coding</p>
    <p :class="classObject">Beauty</p>
  </div>
</template>

<script>
export default {
  data() {
    return {
      class1: false,
      class2: false,
    };
  },
  computed: {
    // 👇 Computed object property
    classObject() {
      return {
        'class-1': this.class1,
        'class-2': this.class2,
      };
    },
  },
};
</script>
...

Add both static and dynamic classes

We can set the class prop on the same element twice, once to add static classes, and once to add the dynamic classes that will be present based on certain conditions.

For example:

<template>
  <div id="app">
    <input
      type="checkbox"
      name="class-1"
      v-model="class1"
    />
    <label for="class-1">Class 1</label>

    <br />

    <input
      type="checkbox"
      name="class-2"
      v-model="class2"
    />
    <label for="class-2">Class 2</label>

    <!-- 👇 Add classes conditionally and statically -->
    <p
      class="static-1 static-2"
      :class="{ 'class-1': class1, 'class-2': class2 }"
    >
      Coding
    </p>
    <p
      class="static-1 static-2"
      :class="{ 'class-1': class1, 'class-2': class2 }"
    >
      Beauty
    </p>
  </div>
</template>

<script>
export default {
  data() {
    return {
      class1: false,
      class2: false,
    };
  },
};
</script>

<style>
.class-1 {
  font-size: 2em;
  font-weight: bold;
}

.class-2 {
  color: blue;
  text-transform: uppercase;
}

/* 👇 Classes to add statically */
.static-1 {
  font-family: 'Segoe UI';
}

.static-2 {
  font-style: italic;
}
</style>
Adding a class conditionally in Vue.
The texts are styled with static classes before being conditionally styled with dynamic classes.

The static-1 and static-2 classes are always applied to the texts, making them italic and changing the font.

How to Convert XML to JSON in JavaScript

We can use the xml-js library from NPM to easily convert XML to JSON in JavaScript, i.e.:

import { xml2json } from 'xml-js';

// ...

const json = xml2json(xml);

For example:

index.js

import { xml2json } from 'xml-js';

const xml = `<name>Garage</name>
<cars>
    <color>red</color>
    <maxSpeed>120</maxSpeed>
    <age>2</age>
</cars>
<cars>
    <color>blue</color>
    <maxSpeed>100</maxSpeed>
    <age>3</age>
</cars>
<cars>
    <color>green</color>
    <maxSpeed>130</maxSpeed>
    <age>2</age>
</cars>`;

const json = xmltojson(xml, );

console.log(json);

This code will have the following output:

{"declaration":{"attributes":{"version":"1.0","encoding":"UTF-8"}},"elements":[{"type":"element","name":"languages","elements":[{"type":"element","name":"language","elements":[{"type":"text","text":"\n      English\n   "}]},{"type":"element","name":"language","elements":[{"type":"text","text":"\n      French\n   "}]},{"type":"element","name":"language","elements":[{"type":"text","text":"\n      Spanish\n   "}]}]}]}

What are XML and JSON?

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.

Install xml-js

Before using xml-js, we need to install it in our project. We can do this with the NPM CLI.

npm i xml-js

Or with the Yarn CLI:

yarn add xml-js

After installation, we’ll be able to import it into a JavaScript module, like this:

import { xml2json } from 'xml-js';

We use import destructuring to access the xml2json() method directly from the library.

For a CommonJS module, we’ll import it like this instead:

const { xml2json } = require('xml-js');

The xml2json() function

The xml2json() function has two parameters. The first is the XML string to convert to JSON, and the second is an optional object.

const json = xml2json(xml, { spaces: 2, compact: true });

Customize conversion of XML to JSON

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);

Here’s the new output:

{
  "declaration": {
    "attributes": {
      "version": "1.0",
      "encoding": "UTF-8"
    }
  },
  "elements": [
    {
      "type": "element",
      "name": "languages",
      "elements": [
        {
          "type": "element",
          "name": "language",
          "elements": [
            {
              "type": "text",
              "text": "\n      English\n   "
            }
          ]
        },
        {
          "type": "element",
          "name": "language",
          "elements": [
            {
              "type": "text",
              "text": "\n      French\n   "
            }
          ]
        },
        {
          "type": "element",
          "name": "language",
          "elements": [
            {
              "type": "text",
              "text": "\n      Spanish\n   "
            }
          ]
        }
      ]
    }
  ]
}

The compact property to determine whether the resulting JSON should be detailed or compact. It is false by default.

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 "compact" option
const json = xml2json(xml, { spaces: 2, compact: true });

console.log(json);

Now the resulting JSON will have a significantly smaller size than before:

{
  "_declaration": {
    "_attributes": {
      "version": "1.0",
      "encoding": "UTF-8"
    }
  },
  "languages": {
    "language": [
      {
        "_text": "\n      English\n   "
      },
      {
        "_text": "\n      French\n   "
      },
      {
        "_text": "\n      Spanish\n   "
      }
    ]
  }
}

How to Detect a Browser or Tab Close Event in JavaScript

To detect a browser or tab close event in JavaScript:

  1. Add a beforeunload event listener to the global window object.
  2. In this listener, call the preventDefault() method on the Event object passed to it.
  3. Set the returnValue property of this Event object to an empty string ('').

JavaScript

window.addEventListener('beforeunload', (event) => {
  event.preventDefault();
  event.returnValue = '';
});

We’ll need to detect the event of a browser or tab close to alert the user of any unsaved changes on the webpage.

We use the addEventListener() method to attach an event handler to any DOM objects such as HTML elements, the HTML document, or the window object.

The beforeunload event is fired right before the window, document, and its resources are about to be unloaded. At this point, the document is still visible, and the event is still cancelable.

We have to call preventDefault() on the Event object the handler receives to show the confirmation dialog in the browser. The preventDefault() method prevents the default action of an event. For the beforeunload event, preventDefault() stops the unloading of the resources, the window, and the document.

This JavaScript code will cause a confirmation dialog to be displayed when the user tries to close the browser or tab. Here’s some sample HTML to use the JavaScript in.

HTML

<a href="codingbeautydev.com">Coding Beauty</a>
<br />
Try to close the tab or browser
A confirmation dialog is shown when the user tries to close the tab.
A confirmation dialog is shown when the user tries to close the tab.
A confirmation dialog is shown when the user tries to close the browser.
A confirmation dialog is shown when the user tries to close the browser.

Note that the beforeunload event is triggered not only when the browser or tab is closed, but also when a link is clicked, a form is submitted, the backward/forward button is pressed, or the page is refreshed.

A confirmation dialog is shown when a link is clicked on the page.
A confirmation dialog is shown when a link is clicked on the page.
A confirmation dialog is shown when the user tries to refresh the page.
A confirmation dialog is shown when the user tries to refresh the page.

How to Get an Input Value On Change in React

To get the value of an input on change in React, set an onChange event handler on the input, then use the target.value property of the Event object passed to the handler to get the input value.

For example:

App.js

import { useState } from 'react';

export default function App() {
  const [message, setMessage] = useState('');

  const handleChange = (event) => {
    // 👇 Get input value from "event"
    setMessage(event.target.value);
  };

  return (
    <div>
      <input
        type="text"
        id="message"
        name="message"
        onChange={handleChange}
      />

      <h2>Message: {message}</h2>
    </div>
  );
}
Getting an input value on change in React.
The message is updated when the input value changes.

Here we create a state variable (message) to track the input value. By setting an onChange event handler, the handler function will get called whenever the text in the input field changes.

The handler function will be called with an Event object containing data and allowing actions related to the event. The target property of this object lets us access the object representing the input element.

This input element object has a value property which returns the text currently in the input field. So we call the setMessage() function with this property as an argument to update the message variable with the input value, and this reflects on the page after the DOM is updated.

The Complete Guide to JavaScript Symbols

In this article, we’re going to learn all about JavaScript symbols and how to use them in practice.

Creating symbols

The Symbol is a data type that was added to JavaScript in ES6. To create a symbol, you can use the global Symbol() function.

const sym1 = Symbol();

The Symbol() function accepts an optional string that serves as the description of the symbol.

const sym2 = Symbol('color');

const sym3 = Symbol('maxSpeed');

const sym4 = Symbol('age');

You can access this description with the description property of the Symbol. If no description was set, this property would be undefined.

console.log(sym1.description); // undefined

console.log(sym2.description); // color

console.log(sym3.description); // maxSpeed

console.log(sym4.description); // age

Every time you call the Symbol() function, it returns a new unique value.

console.log(Symbol() === Symbol()); // false

console.log(Symbol('color') === Symbol('color')); // false

console.log(Symbol('age') === Symbol('age')); // false

As a symbol is a primitive value, using the typeof operator on it will return a symbol string.

console.log(typeof sym1); // symbol

Trying to use the new operator to create a symbol will cause a TypeError. This prevents the creation of an explicit Symbol wrapper object in place of a new symbol value.

const num1 = Number(2);
console.log(typeof num1); // number

const num2 = new Number();
console.log(typeof num2); // object

const bool1 = Boolean(true);
console.log(typeof bool1); // boolean

const bool2 = new Boolean(true);
console.log(typeof bool2); // object

const sym1 = Symbol('color');
console.log(typeof sym1); // symbol

// ❌ TypeError
const sym2 = new Symbol('color');
console.log(typeof sym2);

Sharing symbols

ES6 comes with a global symbol registry that allows you to share symbols globally. To create a symbol for sharing, you use the Symbol.for() method instead of the Symbol() function. Symbol.for() takes a key and returns a symbol value from the registry.

For example:

const color = Symbol.for('color');

The Symbol.for() method first searches the global symbol registry for a symbol with the color key and returns it if it exists. Otherwise, Symbol.for() creates a new symbol, adds it to the registry with the specified color key, and returns the symbol.

Afterward, calling Symbol.for() with the same key returns this same symbol, as it now exists in the registry.

const carColor = Symbol.for('color');

console.log(color === carColor); // true

The Symbol.keyFor() method works together with Symbol.for() to retrieve information from the global symbol registry. You can get the key of a symbol that exists in the registry with Symbol.keyFor().

console.log(Symbol.keyFor(carColor)); // 'color'

If the key doesn’t exist, Symbol.keyFor() returns undefined.

const filename = Symbol('filename');

console.log(Symbol.keyFor(filename)); // undefined;

Uses of symbols

1. Enumerations

With symbols, we can define a group of constants to set a variable to a predefined set of values – an enumeration.

Let’s say we want to create a variable to store a value representing the day of the week. To create an enum, we could use strings like ‘Mon', 'Tue', 'Wed', etc.

const daysOfWeek = {
  Mon: 'Mon',
  Tue: 'Tue',
  Wed: 'Wed',
  Thu: 'Thu',
  Fri: 'Fri',
  Sat: 'Sat',
  Sun: 'Sun',
};

const dayOfWeek = daysOfWeek.Tue;

By using symbols in place of the string, we can ensure that the constants are always distinct from one another, and not worry about duplicate string values.

const daysOfWeek = {
  Mon: Symbol('Mon'),
  Tue: Symbol('Tue'),
  Wed: Symbol('Wed'),
  Thu: Symbol('Thu'),
  Fri: Symbol('Fri'),
  Sat: Symbol('Sat'),
  Sun: Symbol('Sun'),
};

const dayOfWeek = daysOfWeek.Tue;

2. Prevention of property name clashes

Since a symbol is always guaranteed to be unique, we can use it to avoid name clashes when entirely separate sections of code need to store data on an object. The different sections may not be aware of each other, but each wants to make sure that the property it uses is not going to be used by another.

For example:

// module-1.js

const module1 = () => {
  // Prevent external access with closure
  const module1Sym = Symbol();
  return (obj) => {
    // Put some data on obj that this module can access later
    obj[module1Sym] = 'module 1 data';
  };
};

// module-2.js

const module2 = () => {
  // Prevent external access with closure
  const module2Sym = Symbol();
  return (obj) => {
    // Put some data on obj that this module can access later
    obj[module2Sym] = 'module 2 data';
  };
};

// index.js

const obj = {};
module1(obj);
module2(obj);

If the modules each used a string instead of a symbol for the property name, it could cause problems as the names could clash.

For example:

// module-1.js

const module1 = () => {
  return (obj) => {
    // Put some data on obj that this module can access later
    obj.moduleData = 'module 1 data';
  };
};

// module-2.js

const module2 = () => {
  return (obj) => {
    // Put some data on obj that this module can access later
    obj.moduleData = 'module 2 data';
  };
};

// index.js

const obj = {};
module1(obj);
module2(obj);

// 'module 1 data' has been overwritten by the call the module2()

Note that symbol properties are not enumerable, if you call Object.keys() on the object, they will not be included in the array result.

const dayOfWeek = Symbol('dayOfWeek');
const event = {
  [dayOfWeek]: daysOfWeek.Tue,
  description: 'Birthday',
};

console.log(Object.keys(event)); // [ 'description' ]

Object.getOwnPropertyNames() returns an array of all the enumerable and non-enumerable properties of an object, but symbol properties are still excluded.

console.log(Object.getOwnPropertyNames(event)); // [ 'description' ]

To get all the symbol properties, you use the Object.getOwnPropertySymbols() method. This method returns an array of all symbol properties found directly on a given object.

console.log(Object.getOwnPropertySymbols(event)); // [ Symbol(dayOfWeek) ]

Well-known symbols

The Symbol class comes with static properties known as well-known symbols. They are used to implement and customize the behavior of certain built-in JavaScript operations.

Let’s explore some of these well-known symbols:

Symbol.hasInstance

The Symbol.hasInstance method customizes the behavior of the instanceof operator. Generally, when you use the instanceof operator like this:

obj instanceof type

JavaScript will call the Symbol.instanceof method like this:

type[Symbol.hasInstance](obj);

Here’s an example where we use the instanceof method on an instance of a user-defined class.

class List {}

console.log([] instanceof List); // false

Going by the default behavior of instanceof, [] is an Array, not an instance of the List class, so instanceof returns false here.

If we want to change this behavior and make instanceof return true in this scenario, we would customize the Symbol.hasInstance method like this:

class List {
  static [Symbol.hasInstance](obj) {
    return Array.isArray(obj);
  }
}

console.log([] instanceof List); // true

Symbol.iterator

With the Symbol.iterator method, we can specify if and how objects of a class can be iterated over. When this method is present, we will be able to use the for...of loop and spread syntax (...) on the objects of the class.

When you use the for...of loop on an array:

const numbers = [1, 2, 3];

for (const num of numbers) {
  console.log(num);
}

/*
1
2
3
*/

Internally, JavaScript first calls the Symbol.iterator method of the numbers array to get the iterator object. Then it continuously calls the next() method on the iterator object and copies the value property in the num variable, It exits the loop when the done property of the iterator object is true.

var iterator = numbers[Symbol.iterator]();

console.log(iterator.next()); // Object {value: 1, done: false}

console.log(iterator.next()); // Object {value: 2, done: false}

console.log(iterator.next()); // Object {value: 3, done: false}

console.log(iterator.next()); // Object {value: undefined, done: true}

By default, objects of a user-defined class are not iterable. But we can make them iterable with the Symbol.iterator method, as you’ll use in the following example:

class List {
  elements = [];

  add(element) {
    this.elements.push(element);
    return this;
  }

  // Generator
  *[Symbol.iterator]() {
    for (const element of this.elements) {
      yield element;
    }
  }
}

const colors = new List();
colors.add('red').add('blue').add('yellow');

// Works because of Symbol.iterator
for (const color of colors) {
  console.log(color);
}

/*
red
blue
yellow
*/

console.log([...colors]);
// [ 'red', 'blue', 'yellow' ]

Symbol.toStringTag

This symbol lets us customize the default string description of the object. It is used internally by the Object.prototype.toString() method.

class CarClass {
  constructor(color, maxSpeed, age) {
    this.color = color;
    this.maxSpeed = maxSpeed;
    this.age = age;
  }
}

const car = new CarClass('red', 100, 2);

console.log(Object.prototype.toString.call(car));
// [object Object]

Here is the default implementation of Symbol.toStringTag was outputted. Here’s how we can customize it:

class CarClass {
  constructor(color, maxSpeed, age) {
    this.color = color;
    this.maxSpeed = maxSpeed;
    this.age = age;
  }

  get [Symbol.toStringTag]() {
    return 'Car';
  }
}

const car = new CarClass('red', 100, 2);

console.log(Object.prototype.toString.call(car));
// [object Car]

Symbol.toPrimitive

The Symbol.toPrimitive method makes it possible for an object to be converted to a primitive value. It takes a hint argument that specifies the type of the resulting primitive value. This hint argument can be one of 'number', 'string', or 'default'.

Here’s an example of using the Symbol.toPrimitive method.

class Money {
  constructor(amount, currency) {
    this.amount = amount;
    this.currency = currency;
  }

  [Symbol.toPrimitive](hint) {
    if (hint === 'string') {
      return `${this.amount} ${this.currency}`;
    } else if (hint === 'number') {
      return this.amount;
    } else if (hint === 'default') {
      return `${this.amount} ${this.currency}`;
    }
  }
}

const price = new Money(500, 'USD');

console.log(String(price)); // 500 USD
console.log(+price); // 500
console.log('Price is ' + price); // Price is 500 USD

Conclusion

In this article, we learned how to create JavaScript symbols and share them in the global symbol registry. We saw the advantages of using symbols, and how well-known symbols can be used to customize the behavior of built-in JavaScript operations.

How to Get a File Name Without the Extension in Node.js

To get the name of a file without the extension in Node.js, use the parse() method from the path module to get an object representing the path. The name property of this object will contain the file name without the extension.

For example:

const path = require('path');

path.parse('index.html').name; // index

path.parse('package.json').name; // package

path.parse('image.png').name; // image

The parse() method

The parse() method returns an object with properties that represent the major parts of the given path. The object it returns has the following properties:

  1. dir – the directory of the path.
  2. root – the topmost directory in the operating system.
  3. base – the last portion of the path.
  4. ext – the extension of the file.
  5. name – the name of the file without the extension.
path.parse('C://Code/my-website/index.html');

/*
Returns:
{
  root: 'C:/',
  dir: 'C://Code/my-website',
  base: 'index.html',
  ext: '.html',
  name: 'index'
}
*/

If the path is not a string, parse() throws a TypeError.

// ❌ TypeError: Received type of number instead of string
path.parse(123).name;

// ❌ TypeError: Received type of boolean instead of string
path.parse(false).name;

// ❌ TypeError: Received type of URL instead of string
path.parse(new URL('https://example.com/file.txt')).name;

// ✅ Received correct type of string
path.parse('index.html').name; // index