If you haven't read part 1 of this series, check it out.
In DOM manipulation, a common thing to do is to add and remove classes. This is especially helpful when you want a click event or some action on the page to change the look and feel of a component.
You can use the
selection methods we referred to in the last part of this series to select an object and then see the classes using a method called
classList. Within that you can even use more methods such as:
toggle will allow you to turn on/off a class without having to know the status. It will add the class if it's not there, remove it if it is.
remove are pretty obvious but are not aware of the current state like
Attributes are anything added to an element that provides more information to the element.
style, etc... are all examples of attributes.
Thought I'd share something I wasn't aware was that in
alttags, it's unecessary to say
alt="Photo of thing"because the screen reader already informs the user that it's a photo. This creates redundant information that could be shortened to
alt="thing"in this example.
Interesting things you can do with this:
const pic = document.querrySelector('img'); //Selects an img on the page pic.alt = 'Thing' //Allows you to add an alt tag "Thing" console.log(pic.getAttribute('alt'); //logs the alt tag for the img //You can also use this: pic.setAttribute('alt', 'This is a new alt tag'); console.log(pic.getAttribute('alt'); // "This is a new alt Tag"
Custom Attributes are ones that are free form and don't necessarily follow a specific format/convention like the
alt tag mentioned above.
While you can do it, it's not recommended to creat your own custom attributes (which you can do from set attributes). Data attributes are a better use case for this.
HTML from Strings and XSS
const myParagraph = document.createElement('p'); is the most common method of creating HTML elements (this one specifically creates a
<p></p> element. You can use the attributes section above to add attributes to the element. While it's created, it won't show on the page because you will have to append it to the DOM.
Paint that created element on the page using
document.body.appendChild(myParagraph); would do this but add it at the end of the body tag. Note, when you do this, it will cause the browser to do what is called a "reflow", basically a page refresh. If you do this a lot, it can negatively affect the user experience. The way to work around this is to only call the
document.body.appendChild(someTagYouMade) once after appending the element you wanted to create that isn't on the page yet. That was a lot to chew on, let's break that down.
const myDiv = document.createElement('div'); myDiv.classList.add('containter'); document.body.appendChilde(myDiv); const myImage = document.createElement('img'); myImg.src = 'https://google.com'; myImg.alt = 'Cool Photo, Bruh'; document.body.appendChilde(myDiv); const myParagraph = document.createElement('p'); myParagraph.classList.add('specialPara'); myParagraph.textContent = 'I am a p tag'; document.body.appendChild(myDiv);
While you can do the above, it's not DRY (Don't Repeat Yourself). The better way to run this would be to create all the elements and their attributes then append to the body like this:
const myDiv = document.createElement('div'); myDiv.classList.add('containter'); const myImage = document.createElement('img'); myImg.src = 'https://google.com'; myImg.alt = 'Cool Photo, Bruh'; const myParagraph = document.createElement('p'); myParagraph.classList.add('specialPara'); myParagraph.textContent = 'I am a p tag'; document.body.appendChild(myDiv);
This is not only less code to write, but it's a better user experience because the browser doesn't have to repaint the page 3 times.
insertAdjacentText(); like we used on part one? The convention for position is the same where you can use:
'beforebegin': Before the
'afterbegin': Just inside the
element, before its first child.
'beforeend': Just inside the
element, after its last child.
'afterend': After the
afterbegin--> foo <!--
This method clones the node, since we haven't really tackled the tree structure of the DOM to explain the difference between parents, children, and nodes - let's dive into that really quick:
What you can see in the image above is the DOM Tree. This is completely fictious and just a visual representation of what happens in your browser. The DOM tree shows all the elements on the HTML and below them are the children. If you wanted to select the
h1 tag it would be a child of
body and a parent of the
text node "DOM Lesson one".
What this method
cloneNode() does is clone the element you select but it won't clone it's children unless you pass an argument of
true like so:
//Clone h1 from the DOM tree above const heading = document.querySelector('h1'); heading.cloneNode();
What will happen in the example above is that it will copy that
h1 tag but none of the text content inside - specifically "DOM Lesson one", you would accomplish that by running:
//Clone h1 from the DOM tree above const heading = document.querySelector('h1'); heading.cloneNode(true); //note we are going "DEEP" here
Creating HTML using strings
There are some security risks associated with doing this (XSS attacks, more on that in a moment)
In this section, we are going to learn about
<html> <header> </header> <body> <div class="thing> </div> <script>const thing = document.querySelector('.thing'); thing.innerHTML = `
<h1>This is inside of backticks ;) </h1> <img src="https://someimageURL.com" alt"some image"><img> <p> This is the power of backticks, multiline code that maintains,<br> it's spacing!</p> `; </script> </body> </html>
One important thing to point out is that these are strings and not actual HTML. These aren't real elements, at least until the dom is created. This means you cannot dynamically edit these elements unless you select them after they are called then make the change.
Turning a String into an HTML Element
It's a roundabout way but it works!
const stringToHTML = document.createRange().createContextualFragment(thing);
Note, I'm using the
thingvariable from the example before this one as an argument so that it breaks the
thingstring into html elements.
Now they are in the DOM but not on the page so they can be selected and you can use
.appendChild() or any of the methods we covered before.
If you use
innerHTML on your page and a user has an input that renders to the page - they could put HTML or scripts in the form and take control of your page. I'm going to cover this in my future notes. Stay tuned!