Categories
Object-Oriented JavaScript

Object-Oriented JavaScript — DOM Traversal Shortcuts

JavaScript is partly an object-oriented language.

To learn JavaScript, we got to learn the object-oriented parts of JavaScript.

In this article, we’ll look at the DOM.

DOM Access Shortcuts

Using childNodes, parentNode, nodeName, nodeValue, and attributes properties let us traverse the DOM.

However, they’re very inconvenient to use if we have any complex document.

Deeper nodes are hard to access and there’s no easy way to search for the items we want.

We have various shortcut methods that let us find the DOM element we want in an easier way.

The methods include getElementsByTagName(), getElementsByName(), getElementById() , querySelector() , and querySelectorAll() .

The getElementsByTagName method lets us get all the elements with a given tag name.

For instance, with the given HTML:

<body>
  <p class="opener">foo</p>
  <p><em>bar</em> </p>
  <p id="closer">baz</p>
  <!-- the end -->
</body>

We can write:

document.`getElementsByTagName('p');`

to returns a NodeList of p elements.

We can get the number of p elements with the length property:

document.`getElementsByTagName('p').length`

And we can get elements by index:

document.`getElementsByTagName('p')[0]`

We can get the HTML with innerHTML :

document.`getElementsByTagName('p')`[0].innerHTML;

and we get 'foo' .

getElementById lets us get an element by ID.

We can write:

document.getElementById('closer');

to get the element with ID closer .

getElementByClassName lets us get elements by class name.

querySelector lets us get an element with a given CSS selector.

And querySelectorAll lets us get all elements with the given CSS selector.

Siblings, Body, First, and Last Child

The nextSibling and previousSibling properties are 2 convenient properties to navigate the DOM tree if we have reference to one element.

So if we have:

const p = document.getElementById('closer');

We can write

console.log(p.previousSibling)

And get:

#text

And we can use it again by writing:

console.log(p.previousSibling.previousSibling)

and we get the p element above it.

We can mix previousSibling and nextSibling .

For instance, we can write:

p.previousSibling.nextSibling

A DOM node also has the firstChild and lastChild properties.

For instance, we can use:

document.body.firstChild;

to get the first child node of the body.

firstChild is the same as childNodes[0] .

lastChild gets the last child node of a node.

We can use:

document.body.lastChild;

to get the last child node of the body.

lastChild is the same as childNodes[childNodes.length — 1] .

Modifying DOM Nodes

We can modify DOM nodes by assigning a value to the innerHTML property/

For instance, we can write:

const p = document.getElementById('closer');
p.innerHTML = 'closer';

We can also set the HTML string as the value of innerHTML :

const p = document.getElementById('closer');
p.innerHTML = '<em>closer</em> closer';

We can also change the content of a node by assigning a value to the nodeValue property:

const p = document.getElementById('closer');
p.nodeValue = 'closer';

Conclusion

We can get DOM nodes with various shortcut methods.

Also, we can set the content with various properties.

Categories
Object-Oriented JavaScript

Object-Oriented JavaScript — DOM Traversal

JavaScript is partly an object-oriented language.

To learn JavaScript, we got to learn the object-oriented parts of JavaScript.

In this article, we’ll look at the DOM.

Accessing DOM Nodes

We can access DOM nodes with built-in objects in the browser.

The document node gives us access to the current document.

We can see the properties of it with the console.dir method:

console.dir(document);

We can use the nodeType property to get the type of node.

ndoeName has the node’s name.

nodeValue has the value, which is the text for text nodes for example.

The:

document.nodeType;

returns 9.

There’re 12 node types, which are represented by integers.

The most common is 1 for element, 2 attributes, and 3 for text.

document.nodeName

returns '#document' .

And document.nodeValue is null .

documentElement

The document.documentElement property gives us the HTML element.

The nodeType is 1 since it’s an element.

We can see that with:

document.documentElement.nodeType

We can get the nodeName with:

document.documentElement.nodeName

and get the tag name with:

document.documentElement.tagName

They both return 'HTML' .

Child Nodes

We can get child nodes from the document object.

Also, we can check if there’re any child nodes with the hasChildNodes method.

For example, we can write:

document.documentElement.hasChildNodes();

and we get true .

The childNodes property has the length property to get the number of child nodes of a DOM node.

For example, we can write:

document.documentElement.childNodes.length;

And we can get the child nodes by its index:

document.documentElement.childNodes[0];
document.documentElement.childNodes[1];

etc.

To get the parent node, we can use the parentNode property:

document.documentElement.childNodes[1].parentNode;

they all return some element.

In the last example, the HTML element is returned.

For instance, if we have:

<body>
  <p class="opener">foo</p>
  <p><em>bar</em> </p>
  <p id="closer">baz</p>
  <!-- the end -->
</body>

Then console.log(document.body.childNodes.length) logs 10 since we have the HTML elements, attribute nodes, text nodes, and comment nodes.

Attributes

We can check whether an element has attributes with the hasAttributes method.

For instance, we can write:

console.log(document.body.childNodes[1].hasAttributes())

then we get true .

If it’s true then the node has attributes.

We can get the attributes with the attributes .

For instance, we can write:

console.log(document.body.childNodes[1].attributes[0].nodeName)

then we get 'class'.

nodeName get the name of the attribute.

We can also use the nodeValue property to get the attribute value.

So we can write:

console.log(document.body.childNodes[1].attributes['class'].nodeValue)

We can also get the attribute with the getAttribute method:

console.log(document.body.childNodes[1].getAttribute('class'))

Accessing the Content Inside a Tag

We can get content inside a tag.

To do that, we can use the textContent property to get the text content inside a tag.

So we can write:

console.log(document.body.childNodes[1].textContent)

then we get 'foo' .

Also, we can get the innerHTML property to get the HTML content in an HTML element node:

console.log(document.body.childNodes[3].innerHTML)

and we get:

<em>bar</em>

Like other elements, we can get the length , nodeName and nodeValue .

For instance, we can write:

console.log(document.body.childNodes[1].childNodes.length)

to get the length of the child nodes and:

console.log(document.body.childNodes[0].nodeName)

which logs ‘#text’ and:

console.log(document.body.childNodes[1].childNodes[0].nodeValue)

and we get 'foo' .

Conclusion

We can use various properties to get DOM nodes and attributes.

Categories
Object-Oriented JavaScript

Object-Oriented JavaScript — Built-in Browser Objects

JavaScript is partly an object-oriented language.

To learn JavaScript, we got to learn the object-oriented parts of JavaScript.

In this article, we’ll look at built-in browser objects.

The window.location Property

The window.location property lets us get the URL of the page and redirect it to another one.

For instance, we can use location.hostname to get the hostname.

And href gets us the full path.

pathname gets us the segment before the query string.

port gives us the port.

search gives us a query string.

We can get all the properties of the location object with the loop:

for (const key in location) {
  if (typeof location[key] === "string") {
    console.log(key, location[key]);
  }
}

We loop through each property with the location object.

We set the location.href property to redirect to a new URL.

For example, we can write:

window.location.href = 'http://www.example.com';

Also, we can write:

location.href = 'http://www.example.com';
location = 'http://www.example.com';
location.assign('http://www.example.com');

replace is almost the same as assign , but it doesn’t create a new browser history entry.

We can use it by writing:

location.replace('http://www.example.com');

To reload a page, we can write:

location.reload();

We can also assign window.location.hre to itself to reload the page:

window.location.href = window.location.href;
location = location;

The window.history Property

The window.history property lets us access the previously visited pages of the same browser session.

For instance, we can see the number of pages visited before visiting the current page with window.history.length .

We can’t see the actual URLs to maintain privacy.

But we can navigate back and forth through the user’s session.

We can use:

history.forward();
history.back();

to move forward and back in the history respectively.

history.back() is also the same as history.go(-1); .

To go 2 pages back, we can write:

history.go(-2);

And we can reload the current page with:

history.go(0);

The HTML5 history API also lets us change the URL without reloading the page.

We can use the history.pushState method to change the page.

For instance, we can write:

history.pushState({foo: 1}, "", "hello");

The first argument is the value of the stte property.

The 2nd is the title, which isn’t used.

And the 3rd is the URL path to go to.

Then we can get the state with history.state .

The window.frames Property

The window.frames property is a collection of all frames in the current page.

It doesn’t distinguish between frames and iframes.

window.frames points to window no matter frames are present on the page or not.

So:

window.frames === window;

returns true .

If we have a frame like:

<iframe name="helloFrame" src="hello.html" />

Then frames.length is 1.

We can get the first frame, which is window , with:

window.frames[0];
window.frames[0].window;
window.frames[0].window.frames;
frames[0].window;
frames[0];

We can reload the frame with:

frames[0].window.location.reload();

And the frame’s parent is window , so:

frames[0].parent === window;

returns true .

We have the top property to get the topmost page, which is the page all the other frames from within the frame.

So all of these:

window.frames[0].window.top === window;
window.frames[0].window.top === window.top;
window.frames[0].window.top === top;

return true .

self is the same as window , so:

self === window

returns true .

Also, these also return true :

frames[0].self === frames[0].window;
window.frames['helloFrame'] === window.frames[0];
frames.helloFrame === window.frames[0];

Conclusion

The window.location property and window.frames lets us get and set the URL of our app and get the frames respectively.

Categories
Object-Oriented JavaScript

Object-Oriented JavaScript — Prototypes

JavaScript is partly an object-oriented language.

To learn JavaScript, we got to learn the object-oriented parts of JavaScript.

In this article, we’ll look at object prototypes.

Prototype

Every object has a prototype.

Objects inherit from objects call prototypes.

The Prototype Property

The prototype is located in the prototpe property for constructor functions.

Constructor functions have the prototype property that has the methods that returned with the constructor instance.

For instance, if we have a function:

function foo(a, b) {
  return a + b;
}

Then we can get its prototype by writing:

console.log(foo.prototype)

It has the constructor property, which is the function itself.

This property is always available as long as we don’t specify otherwise.

Adding Prototype Methods and Properties

We can add properties and methods to the prototype, then they’ll be shared with all instances of the constructor.

For instance, we can write:

function Person(name) {
  this.name = name;
  this.whoAreYou = function() {
    return `I am ${this.name}`;
  };
}

Then we have the the name and whoAreYou instance properties.

This will create different copies of the properties for each instance.

But we can share whoAreYou between different instances since it’s the same method.

We can do that by putting it in the prototype property:

function Person(name) {
  this.name = name;
}

Person.prototype.whoAreYou = function() {
  return `I am ${this.name}`;
};

this.name should be unique between different instances, so it should be in the constructor.

But whoAreYou can be shared, so we add it as a property of the prototype .

Using the Prototype’s Methods and Properties

We can use the prototype’s methods and properties by instantiating the instance of the constructor.

For instance, we can write:

const jane = new Person('jane');

Then we can call whoAreYou by writing:

console.log(jane.whoAreYou())

Then we get:

'I am jane'

Own Properties vs Prototype Properties

Own properties are properties that are defined within the object itself rather than inherited.

Prototype properties are properties that are inherited from another object.

In the Person constructor, we have the prototype.whoAreYou method, which means it’s inherited from the prototype of the Person constructor.

When we get a property or call a method, the JavaScript engine looks through all the properties of the objects in the prototype chain to get the property.

We can’t tell the difference just by looking at the invocation.

So jane.name looks like jane.whoAreYou() but the first is an own property and the 2nd is a prototype property.

We can check by using the constructor property.

For instance, we can write:

console.log(jane.constructor.prototype);

We see the whoAreYou method in the logged object.

Overwriting a Prototype’s Property with an Own Property

We can overwrite a prototype’s property with an own property.

For instance, we can write:

function Person(name) {
  this.name = name;
}

Person.prototype.name = 'no name';

Then if we create a Person instance:

const jane = new Person('jane');

We see that jane is:

{name: "jane"}

We can determine if a property is an own property with the hasOwnProperty method.

For instance, if we write:

console.log(jane.hasOwnProperty('name'));

We get true .

We can delete a property with the delete operator.

For instance, we can write:

delete jane.name

to remove it.

Conclusion

An object’s prototype is what an object inherits from.

We can override its properties.

Categories
Object-Oriented JavaScript

Object-Oriented JavaScript — Prototype Catches

JavaScript is partly an object-oriented language.

To learn JavaScript, we got to learn the object-oriented parts of JavaScript.

In this article, we’ll look at object properties.

Enumerating Properties

We can enumerate properties of an object with the for…in loop.

For example, if we have:

const obj = {
  a: 1,
  b: 2
};

We can write:

const obj = {
  a: 1,
  b: 2
};

for (const key in obj) {
  console.log(key, obj[key]);
}

Then we get:

a 1
b 2

The key has the key and obj[key] has the value.

Not all properties are enumerable, we can set the ebumerable property descriptor to make a property not enumerable.

We can check with the propertyIsEnumerable() method to check if a property is enumerable.

We can do the check with:

console.log(obj.propertyIsEnumerable('a'))

then we should get true since object properties are enumerable unless it’s specified otherwise.

Using isPrototypeOf() Method

Objects also has the isPrototypeOf method that tells whether the specific object is used as a prototype of another object.

For instance, if we have:

const monkey = {
  hair: true,
  feeds: 'bananas',
  breathes: 'air'
};

function Person(name) {
  this.name = name;
}

Person.prototype = monkey;

const james = new Person('james');
console.log(monkey.isPrototypeOf(jane));

Then the console log should log true since we set monkey to the prototype of the Person constructor.

proto Property

Since ES6, the __proto__ property is a standard property to let us set the prototype of an object.

It can also be used as a getter to get the property.

For instance, we can write:

const monkey = {
  hair: true,
  feeds: 'bananas',
  breathes: 'air'
};

function Person(name) {
  this.name = name;
}

Person.prototype = monkey;

const james = new Person('james');
console.log(james.__proto__ === monkey);

Then we get true since we have the prototype of the james object is monkey as we set with:

Person.prototype = monkey;

We can get the same result with:

james.constructor.prototype

We can also set the prototype of an object with it, so we can write:

const monkey = {
  hair: true,
  feeds: 'bananas',
  breathes: 'air'
};

const person = {
  name: 'bob',
  __proto__: monkey
}

Then we’ll get the prototype with the __proto__ property or we can use Object.getPrototypeOf() to check:

Object.getPrototypeOf(person)

then we get the monkey object.

Augmenting Built-in Objects

We can add functionality to built-in objects.

We can use polyfills to add standard functionalities to objects that aren’t available in some environments.

Issues with Prototype

We should know that the prototype chain is live.

It’ll change when we change the proottype object.

The prototype.constructur property isn’t available.

We can set the constructor top whatever we want.

If we change the prototype, then we have to change the constructor.

For instance, we can write:

function Dog() {}

Dog.prototype = {
  paws: 4,
  hair: true
};

const dog = new Dog();
console.log(dog.constructor === Dog);

then we get false .

We’ve to set the constructor to the Dog to make it true so it reports correctly:

Dog.prototype.constructor = Dog;

Now:

console.log(dog.constructor === Dog);

logs true like we expect.

Conclusion

We’ve to be careful when working with prototypes.

We can set it and we may sometimes get results we may not expect.