owned this note
owned this note
Published
Linked with GitHub
# DOM Manipulation
After this lesson, you should be able to:
- Create, change and delete DOM elements content
- Create, change and delete DOM elements attributes
- Create, change and delete DOM elements
- Create and assign events to DOM elements
## Introduction to DOM Manipulation
Until now, all our HTML pages were static pages. Once the HTML is created and the CSS styles are applied, the page will always look the same.
In this Learning Unit we will learn how to manipulate the DOM elements, creating dynamic pages that respond to user interaction through dynamic forms, buttons and more.
## Element content manipulation
During the execution of our application, the users will interact with the page, and this interactions will produce changes in the DOM. One of this changes is to change the elements's content.
### Recap: how to select an element
To manipulate an HTML element, first we will need to select it.
Let's practice a little bit using some real elements. Create files `index.html` and `index.js` and copy the code below where appropriate:
```htmlmixed=
<!-- index.html -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<script src="index.js"></script>
<title>Document</title>
</head>
<body>
<p id="paragraph">Which is your name?</p>
<a href="#" id="google-link" class="link">Google</a>
<div id="content">
<h1 id="title">Main title</h1>
<ul id="item-list"></ul>
</div>
<button id="add-item-button">Add item</button>
</body>
</html>
```
```javascript=
// index.js
var paragraph = document.getElementById('paragraph');
```
In the JavaScript above, to select the paragraph element *-line 11-*, we will use the `document.getElementById` function. This function will return the selected `p` node.
We can also select an element by its className. But be careful with this method. It will **always return an array of DOM nodes**, no matter if it finds one or more elements in the code.
```javascript
var links = document.getElementsByClassName('link');
```
Finally, we can select an element by its tag Name. This selector will return an array of nodes as well, even if there is only one element.
```javascript
var divs = document.getElementsByTagName('div');
```
The fact that the `document.getElementById` returns a single element makes sense. Remember, in HTML, you use the `id` tag to reference just one element in the document. Correspondingly, the `classes` and the `tags` can be used several times. So it becomes clear that the functions to get elements by class and tag should return a collection of elements.
:::success
:eyeglasses: **Hint:** A useful trick to distinguish when a selector function will return an array of elements or it will return a single element is to take a look at the function name:
- The method `getElementById` returns always one element -the first one found in the code. That's why the "Element" word in the name is **singular**.
- The methods `getElementsByClassName` and`getElementsByTagName` they return always an array of objects. The "Elements" word in the name is **plural**.
:::
## Operating with attributes
### Getting an element attribute content
To get the value of an element, we will use the function `getAttribute`.
```javascript
var attribute = element.getAttribute(attributeName);
```
The function above receives the name of the attribute we are looking for in the element that calls the function and returns the value of the specified attribute. If the attribute does not exist, the function will retun `null` or an empty string `""`.
```javascript
// index.js
var paragraph = document.getElementById('paragraph');
var paragraphId = paragraph.getAttribute('id');
console.log(paragraphId); //=> "paragraph"
```
### Changing the value of an element's attribute
To change the value of an existing attribute on the specified element, call the function `setAttribute`.
```javascript
element.setAttribute(name, value);
```
In this function, `id` is the name of the attribute as a string and the `value` is the desired new value of the attribute. The `setAttribute` function returns `undefined`.
```javascript
// index.js
var paragraph = document.getElementById('paragraph');
contentDiv.setAttribute('id', 'info-paragraph');
```
### Exercise
::: info
Set the `href` attribute of the `a` element identified as *google-link* to `http://www.google.com`
:::
### Creating a new element's attribute
The `setAttribute` function is also useful to create a new attribute for an element. When we call the function, it will look for the attribute specified in the first parameter `name` in the element. If the attribute is found, it will change the value. If not, it will create a new attribute using the name and the desired value.
```javascript
// index.js
var googleLink = document.getElementById('username-input');
contentDiv.setAttribute('name', 'username');
```
### Exercise
::: info
Complete your `a` element with a target attribute. Set the value to `"_blank"` to open the new page in a new tab of your browser.
:::
### Removing an existing element's attribute
The function `removeAttribute` allow us to remove an element's attribute.
```javascript
element.removeAttribute(attrName);
```
This function receives one parameter: `attrName`, a string that sets the name of the attribute we want to remove from `element`and returns . Attempting to remove an attribute that is not on the element doesn't raise an exception.
As we will probably use the same CSS style for all the paragraphs in the webpage, let's remove the `class` attribute from the `p` element:
```htmlmixed
<p class="paragraph">This is the text content</p>
```
We might select the element by its `class` or by its `id`. In this case, we will use `getElementById` because it won't return an array and it will be easier to change.
Remember that we could select all the elements with the same class using `getElementByClass`. This would be useful to apply the same CSS styles to several elements or manipulate them at the same time.
```javascript
// index.js
var paragraph = document.getElementById('paragraph');
paragraph.removeAttribute('id');
paragraph.setAttribute('class', 'paragraph');
```
### Exercise
::: info
Remove completely the `class` attribute to the paragraph element.
:::
## Create a DOM object
You can also create elementsin the DOM using `document.createElement(tagName);`
```javascript
var element = document.createElement('h2');
=> undefined
element
=> <h2></h2>
```
- `element` is the created element object.
- `tagName` is a string that specifies the type of element to be created. The nodeName of the created element is initialized with the value of `tagName`. Don't use qualified names (like "`html:a`") with this method.
### Add onto the page
Let's add a content to an element:
```javascript
element.innerHTML = "Elephant";
```
Let's add an element into a parent element:
```javascript
var parent = document.getElementsByTagName('body')[0]
parent.appendChild(element);
```
- `parent` is the parent element.
- `element` is the child node to append.
Entire elements or nodes can also be manipulated. This is a powerful functionality to create dynamic web pages by adding, changing and deleting elements or even whole sections using JavaScript functions.
### Create a text node
The text inside an element, inside a `p` tag for instance, could be added with `innerHTML`. But there is a more correct way to add text to an element.For this, we will use `createTextNode`.
```javascript
var text = document.createTextNode(data);
```
In the function above, `text` is a Text node and `data` is always a string to put in the text node. The function returns the new node.
```javascript
var text = document.createTextNode('This text is created dynamically');
```
Finally, you just need to add it into a parent node. No matter the type of element, you can insert a `TextNode` in any element.
### Exercise
::: info
Create an empty HTML file:
```htmlmixed=
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
</body>
</html>
```
Create two nodes in your Javascript file:
- A `p` element.
- A text node. It will have "Hi there! I am using Javascript" as value.
Then, add the text into the `p` tag and finally add it to the DOM
:::
#### Adding an element before another element
Inserting elements at the end could be a little limited, don't you think? What if we wanted to insert an element at any order inside our DOM? We can achieve this with the function `insertBefore`.
The `insertBefore` function allow us to select an existing element and insert another before that selected element. For example, if we wanted to introduce a `p` node before the existing `div#first-child`:
```htmlmixed=
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<div id="parent">
<div id="first-child"></div>
</div>
</body>
</html>
```
We need to get the parent node. Then, we select the element that it will be right after our created element, in this case the `div#first-child`. Finally, we will create the node we want to add to the DOM and use `insertBefore` to place it **before** the selected child element.
```javascript
var parent = document.getElementById('parent');
var firstChild = document.getElementById('first-child');
var pTag = document.createElement('p');
var text = document.createTextNode('This text is created dynamically');
pTag.appenChild(text);
parent.insertBefore(pTag, firstChild);
```
This code will produce the next result in your HTML (check it in the inspector after executing your Javascript):
```htmlmixed=
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<div id="parent">
<p>This text is created dynamically</p> <!-- Element created dinamically -->
<div id="first-child"></div>
</div>
</body>
</html>
```
#### Your turn
::: info
Insert a `p` node before the `div#parent`. It should also show the text `"Hi there! I am using Javascript"` inside.
:::
### Remove an existing element
To remove an existing element in the actual DOM, we will need to use the function `removeChild`. The function will apply to any DOM element and it will accept 1 parameter: the selected element child.
```htmlmixed=
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<div id="parent">
<p id="text">This text will be removed</p> <!-- Element created dinamically -->
</div>
</body>
</html>
```
We need to select the parent node. Then, call the function `removeChild` passing the element we want to remove as a parameter.
```javascript
var container = document.getElementById('content');
var pTag = document.getElementById('text');
container.removeChild(pTag);
```
#### Your turn
::: info
Remove the previous `p` node you inserted before.
:::
### Clear an existing element
If we wanted to delete everything inside an element we had to get one by one all the element children inside, right? Well, here is when `innerHTML` appears.
The function `innerHTML` **returns everything inside the element**, not only text. This means all the HTML inside, including tags, will be returned. So, if we wanted to clear the entire `div#content`:
```htmlmixed=
<!-- index.html -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<script src="index.js"></script>
<title>Document</title>
</head>
<body>
<p id="paragraph">Which is your name?</p>
<a href="#" id="google-link" class="link">Google</a>
<div id="content">
<h1 id="title">Main title</h1>
<ul id="item-list"></ul>
</div>
</body>
</html>
```
```javascript
var contentDiv = document.getElementById('content');
contentDiv.innerHTML = "";
```
#### Your turn
::: info
Clear the whole `body` of your HTML page.
:::
## Events in Javascript elements
There are a lot of times where we want to give the users of our web application the hability to interact with us, for example, clicking on a button, responding to a typing, dragging a box, etc
All this interactions are called **Events**, and this events are attached to the DOM elements. There are plenty of them, and now we are going to learn how to attach one to an element.
### Assign events to DOM elements
First we need to do is to select the element we want the event to be attach. Then, we are going to select the event for that element. In this case, we want to create a `click` event for the `button#add-item-button`.
```htmlmixed=
<!-- index.html -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<script src="index.js"></script>
<title>Document</title>
</head>
<body>
<button id="add-item-button">Add item</button>
</body>
</html>
```
```javascript
var button = document.getElementById("add-item-button");
button.click = function(){
console.log("adding an element to the list");
}
```
We create a `click event` assigned to the button `add-item-button`. Then, we have assigned a new anonymous function to that `click event`. This anonymous function will be executed once the event is fired, in this case when the button is clicked.
```javascript
var button = document.getElementById("add-item-button");
button.click = function(){
console.log("adding an element to the list");
}
```
#### Your turn
```htmlmixed=
<!-- index.html -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<script src="index.js"></script>
<title>Document</title>
</head>
<body>
<ul id="items-list">
</ul>
<button id="add-item-button">Add item</button>
</body>
</html>
```
::: info
Create a click event for the `button#add-item-button`. This event will add a `li` element with "Item number " + item number:
:::
```htmlmixed=
<!-- index.html -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<script src="index.js"></script>
<title>Document</title>
</head>
<body>
<ul id="items-list">
<li>Item number 1</li> <!-- Element created dinamically -->
<li>Item number 2</li> <!-- Element created dinamically -->
<li>Item number 3</li> <!-- Element created dinamically -->
<li>Item number 4</li> <!-- Element created dinamically -->
<li>Item number 5</li> <!-- Element created dinamically -->
</ul>
<button id="add-item-button">Add item</button>
</body>
</html>
```
## Inputs manipulation
It is really common to deal with forms in web pages. In this section we will see how to manage with inputs.
### Getting an input current value
Create an HTML document with this structure:
```htmlmixed=
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<label for="username">Name</label><input name="username" type="text">
<button>Send</button>
</body>
</html>
```
It will show the next page:
![](https://i.imgur.com/VWUHhdV.png)
If we wanted to get the value on that input we need to get the element first, and then call to the attribute `value`. By default, if the input does not have any text introduced it will return an empty string.
```javascript
var input = document.getElementsByTagName('input')[0];
console.log(input.value); //=> ""
```
The problem is we normally send the information once the inputs are filled, right? Let's create a `click event` for the button:
```javascript
var button = document.getElementsByTagName('button')[0];
button.onclick = function(){
var input = document.getElementsByTagName('input')[0];
console.log(input.value);
}
```
This time, the code will wait for the user to click the button. Try to add some text inside the input and click the button to send the information you just filled. It will print what you actually have inside the input!
:::warning
:warning: Remember to place the inputs with no forms. Otherwise, the page will be refreshed and all your operations will be lost.
:::
### Getting the current node object
When operating with events, sometimes we need to know which of the assigned nodes or DOM elements were clicked. Now we present you the `event` object.
The `event` object (sometimes referred as `e`) is an object inherit in every event we create. It has a lot of properties and you can check the full list of [event properties](https://developer.mozilla.org/en-US/docs/Web/API/Event#Properties) here.
For now, let's focus on `currentTarget`:
```javascript
var liTags = document.getElementsByTagName('li');
liTags.forEach(function(li){
li.onclick = function(e){
console.log(e.currentTarget.innerHTML);
}
});
```
The `currentTarget` function returns the node where the event was fired. In this case, we had a list of `li` elements and we assigned an `onclick` event to them.
Once this function is executed, it will print the clicked `li` content (remember `innerHTML` returns a DOM element content).
## Summary
In this Learning Unit we learnt about the DOM elements and how to create, modify and delete any of them and their different parts: contents, attributes and the element itself.
We learnt about events and how to add them to any element in the DOM. Also, we learnt about the `event` object and how to manage with it.
## Other Resources
- [Handling events for multiple elements](https://www.kirupa.com/html5/handling_events_for_many_elements.htm)
- [Attaching events for dynamic elements](https://toddmotto.com/attaching-event-handlers-to-dynamically-created-javascript-elements/)