` element, it's extremely difficult to catch all of the defaults a browser might apply to the original tag that may enhance the experience of someone that uses a screen-reader.
>
> This is all to say, unless you have a **really** good reason for using `role` rather than an appropriate tag, stick with the related tag. Just as any other form of engineering, properly employing HTML requires nuance and logic to be deployed at the hand of the implementing developer.
# Element Metadata {#interacting-with-elements-using-js}
If you've ever written a website that had back-and-forth communication between HTML and JavaScript, you're likely aware that you can access DOM elements from JavaScript: modifying, reading, and creating them to your heart's content.
Let's look at some of the built-in utilities at our disposal for doing so:
- [The `document` global object](document-global-object)
- [The `Element` base class](element-class)
- [The event system](#events)
## Document Global Object {#document-global-object}
[As mentioned before, the DOM tree must contain one root node](#the-dom). This node, for any instance of the DOM, is the document entry point. When in the browser, this entry point is exposed to the developer with [the global object `document`](https://developer.mozilla.org/en-US/docs/Web/API/Document). This object has various methods and properties to assist in a meaningful way. For example, given a standard HTML5 document:
```html
This is a page title
This is the page body
and it contains
a lot of various content within
the DOM
```
The `document` object has the ability to get the `` node ([`document.body`](https://developer.mozilla.org/en-US/docs/Web/API/Document/body)), the `` node ([`document.head`](https://developer.mozilla.org/en-US/docs/Web/API/Document/head)), and even the doctype ([`document.doctype`](https://developer.mozilla.org/en-US/docs/Web/API/Document/doctype)).

### Querying Elements
Besides containing static references to `` and ``, there is also a way to query any element by using CSS selectors. For example, if we wanted to get a reference to the single element with the `id` of `mainText`, we could use the CSS selector for an id, combined with [the `querySelector` method on the `document`](https://developer.mozilla.org/en-US/docs/Web/API/Document/querySelector):
```javascript
const mainTextElement = document.querySelector('#mainText');
```
> The `#` in the `#mainText` is the CSS selector syntax for selecting an element based on its `id`. If you had a CSS selector of `#testing`, you'd be looking for an element with the following attribute value:
>
> ```
> id="testing"
> ```
This method will return a reference to the element as rendered in the DOM. [While we'll be covering more of what this reference is able to do later](#element-class), for now we can execute this quick bit of code to show that it's the element we intended to query:
```javascript
console.log(mainTextElement.innerHTML); // This will output the HTML that we used to write this element
```

We also have the ability to gain a reference to many elements at once. Given the same HTML document as before, let's say we want to see how many elements have the `bolded` class applied to it. We're able to do so using [the `querySelectorAll` method on the `document`](https://developer.mozilla.org/en-US/docs/Web/API/Document/querySelectorAll).
```javascript
const boldedElements = document.querySelectorAll('.bolded');
console.log(boldedElements.length); // Will output 2
console.log(boldedElements[0].innerHTML); // Will output the HTML for that element
```

> It's worth mentioning that the way `querySelector` works is not the same [way that the browser checks a node against the CSS selector data when the browser "visits" that node](#how-the-browser-uses-the-dom). `querySelector` and `querySelectorAll` work from a more top-down perspective where it searches the elements one-by-one against the query. First, it finds the top-most layer of the CSS selector. Then it will move to the next item and so-on-so forth until it returns the expected results.
## Element Base Class {#element-class}
While `innerHTML` has been used to demonstrate that the element that's gathered is in fact the element that was queried, there are many _many_ more properties and methods that can be run on an element reference.
When an element is queried and returned, you're given a reference to that element through the [`Element` base class](https://developer.mozilla.org/en-US/docs/Web/API/Element). This class is what contains the properties and methods that you can use to access and modify the element's metadata.
For example, let's say that I wanted to see the width and height an element has when rendered on screen. [Using the `Element.prototype.getBoundingClientRect` method](https://developer.mozilla.org/en-US/docs/Web/API/Element/getBoundingClientRect), you can get all of that information and more:
```javascript
const mainTextElement = document.querySelector('#mainText');
console.log(mainTextElement.getBoundingClientRect());
// Will output: DOMRect {x: 8, y: 16, width: 638, height: 18, top: 16, …}
```
> While the explanation behind the `Element.prototype` is a lengthy one (an article on its own to be sure), suffice it to say that there's a base class for all element references found using `querySelector`. This base class contains a myriad of methods and properties. The `.prototype` loosely refers to those properties and methods in question.
>
> This means that all queried elements will have their own `getBoundingClientRect` methods.
### Attributes {#html-attributes}
[As covered earlier, elements are able to have _attributes_ that will apply metadata to an element for the browser to utilize.](#accessibility) However, what I may not have mentioned is that you're able to read and write that metadata, as well as applying new metadata, using JavaScript.
Let's take a slightly modified example from [the correct tags section](#accessibility) to demonstrate:
```html
```
We could update this list to include the `role`s and `aria-label`s in order to make this non-semantic HTML more relevant in terms of how it reflects its metadata to the browser.
This metadata that we place directly on the elements themselves are called `attributes` and are part of the HTML specification (also referred to as the HTML API in this document). This metadata can be accessed and modified from JavaScript by using the `Element`'s [`getAttribute`](https://developer.mozilla.org/en-US/docs/Web/API/Element/getAttribute) to read the key-value pairing and [`setAttribute`](https://developer.mozilla.org/en-US/docs/Web/API/Element/setAttribute) to set the value to that attribute on an element.
Let's look at how we can set the `role` and `aria-label`s in the DOM using JavaScript:
```javascript
const divToListEl = document.querySelector('#divToList');
// Get the `role` attribute to demonstrate that there's no currently present role
console.log(divToListEl.getAttribute('role')); // `null`
// Let's set a role that emulates a `list`
// Set the value from the HTML API using the Element method `setAttribute`
divToListEl.setAttribute('role', 'list');
// And let's add an aria-label, for good measure
divToListEl.setAttribute('aria-label', 'My favorite fruits');
// Get the value from the HTML API using the Element method `getAttribute`
console.log(divToListEl.getAttribute('role')); // `'list'`
// Using the CSS selector to get the children of the divs
const listItems = document.querySelectorAll('#divToList > *');
// Now, for all of the items in that list, let's use an aria `role` to make them reflect as listitems in their metadata to the browser
for (var i = 0; i < listItems.length; i++) {
listItems[i].setAttribute('role', 'listitem');
}
```
Once this is run, if you inspect the elements tab in your debugger, you should be left with HTML that looks like this:
```html
```
... which is significantly more accessible for users that utilize screen readers, [as mentioned previously](#accessibility). You'll notice that despite not having any of the ARIA attributes prior, the `setAttribute` was able to implicitly create them with the newly placed values.
### Properties {#element-properties}
[As mentioned in a prior section, elements also have properties and methods associated with the instance of the underlying base class](#element-class). These properties are different from attributes as they are not part of the HTML specification. Instead, they're standardized JavaScript `Element` API additions. Some of these properties are able to be exposed to HTML and provide a two-way binding to-and-from the HTML API and the JavaScript `Element` API.
> Unfortunately, for various historical reasons, the list of properties that support this bi-directional mapping between the `Element` API and the HTML API is sporadic and inconsistent. Some elements that support a mapping between the two APIs even only support uni-directional mapping where updating one will not update another.
>
> This is a round-about way of saying, "It is confusing and complicated what properties have attribute bindings and which don't and why. It's okay if you don't get it right away". Even seasoned developers might not be aware of some of the limitations. That all said, let's continue on with some examples that _do_ follow the bi-directional implicit API mapping to showcase how it works and learn more about properties.
For example, if you have [the style attribute](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/style) associated with an element you're working with, you're able to read the values of the element:
```html
This element is green
```
```javascript
// index.js
const greenElement = document.querySelector('#greenEl');
console.log(greenElement.style.backgroundColor); // 'green'
```

Not only are you able to read the value in question, but you can write and edit them as well:
```javascript
greenElement.style.backgroundColor = 'red';
```
Will turn the element's background color red, for example.

Somewhat silly, seeing as how the `
` is no longer green. 🤭
#### Limitations {#attribute-limitations}
While attributes can be of great use to store data about an element, there's a limitation: Values are always stored as strings. This means that objects, arrays, and other non-string primitives must find a way to go to and from strings when being read and written.
> While you've seen `style` attribute be read and written to by an object interface, if you inspect the element or use the `getAttribute` to access the attribute's HTML API value, you'll find that it's really a string with a pleasant API wrapped around it that lets you use an object to interface with the attribute value.
>
> ```javascript
> console.log(mainTextElement.getAttribute('style')); // This will return a string value, despite the API that lets you use an object to read and write
> ```
>
> The reasoning behind this incongruity is due to [the implicit mapping of the HTML API and the `Element` API, as mentioned at the start of the previous section](#element-properties). The limitations described here will also apply to the HTML API of those types of properties.
For example, we can [use `data` attributes](https://developer.mozilla.org/en-US/docs/Learn/HTML/Howto/Use_data_attributes) in order to read and write values via attributes to any given element.
```html
```
```javascript
// index.js
const listEl = document.querySelector('#list');
console.log(listEl.dataset.listitems); // '2'
listEl.dataset.listitems = 3;
console.log(listEl.dataset.listitems); // '3'
```

Note that I wrote the string `'3'` instead of the numerical value `3` in the code sample's outputs in the comments despite using the numerical `3` to set the value. This behavior is due to how default non-string values are saved to attributes.
By default, the primitive's `toString` will be called to store values.
```javascript
element.dataset.userInfo = {name: "Tony"};
console.log(element.dataset.userInfo); // "[object Object]"
/**
* "[object Object]" is because it's running `Object.prototype.toString()`
* to convert the object to a string to store on the attribute
*/
```
> If you're having a difficult time understanding why `toString` is bring run or what `prototype` is doing here, don't worry; you're in good company. The JavaScript prototype system is complex and can be difficult to follow.
>
> For now, it will suffice just to know that you're only able to store strings in an element attribute.
## Events {#events}
Just as your browser uses the DOM to handle on-screen content visibility, your browser also utilizes the DOM for knowing how to handle user interactions. The way your browser handles user interaction is by listening for _events_ that occur when the user takes action or when other noteworthy changes occur.
For example, say you have a form that includes a default `
` element. When that button is pressed, it fires a `submit` event that then _bubbles_ up the DOM tree until it finds a [`