This is the second part of the Learn to Build Object-Oriented JavaScript App using ES6 Classes series. You can check out the first part here. In the whole series, I am building an Address Book 📕App to demonstrate concepts like Object-orientation with ES6, MVC and other best practices when building a JavaScript-based application.
Here is the Address Book App Series
01 Address Book – Get All Contacts
02 Address Book – Get a Contact
03 Address Book – Add a Contact
04 Address Book – Remove a Contact
05 Address Book – Edit a Contact
What’s covered in this article?
- How to add a click event to each contact list item.
- How to show the detailed contact data on the right, which is the details module view, when li is clicked.
- How to show the first contact item on the details module view on load by reusing a method as well as the active state.
- How to get the active state when list item is clicked.
Let’s add the contact details module HTML code in index.html file.
<!-- contact item details module -- >
<section>
<div id="contact-item-details">
loading...
</div>
</section>
Then, go back to app.js and add an Event listener to li element inside renderContactListModule() method before appending li to ul on the AddressBookView class.
$li.addEventListener("click", this.renderContactDetailsModule);
As you can see, when li is clicked, renderContactDetailsModule() method will be invoked. Let’s create this method inside AddressBookView class as well.
renderContactDetailsModule(e) {
// code goes here
}
Inside renderContactDetailsModule() method, I am going to get the selected index value using the parameter (e).
let selectedIndex = null;
if (typeof e === 'object') {
e.stopPropagation();
selectedIndex = this.getAttribute('data-index')
} else {
selectedIndex = e;
}
I have declared a variable selectedIndex and set it to null as it’s initial value. This variable will hold the selected index value later.
Next, I have an if condition in which I use typeof method to determine how the renderContactDetailsModule() is being invoked.
If renderContactDetailsModule() is invoked by a click event, e would be an object. In that case, I can get the selected li index value from it’s data-index attribute which is added to each li on the renderContactListModule() method and assign to selectedIndex.
If e parameter is an object, we need to use e.stopPropagation() in order to avoid event bubbling.
Note: e.stopPropagation() will prevent parent click event when child clicks event trigger. Later we will be adding a couple of child elements like edit and delete buttons inside li. At that time, we do not want to parent click event trigger (li) when we click the child click event (edit button).
If e is not an object, we can simply assign the value of e to selectedIndex as it would be an integer. We will see later where we can call this method by passing an index value as an argument.
Let’s add the getContact() method to our AddressBookCtrl class.
// AddressBookCtrl class
getContact(index) {
return contactsData[index];
}
The getContact() method will receive an index number as a parameter and will return a single contact object from the contactsData array based on the index value that we pass as an argument.
Now we can use getContact() inside the renderContactDetailsModule() method in order to get the selected contact object by passing the selectedIndex.
const selectedItem = addressBookApp.getContact(selectedIndex);
Now I need to get the contact details module and cache it.
const $ContactItemUI = document.getElementById('contact-item-details');
Finally, assign the formatted string to the DOM element using its innerHTML property. As you can see, I use some of the ES6 features. One is backtic which is used at the beginning and end of the string that allows creating a multi-line string. Another one is ${} format that allows us to add variables inside a string aka string interpolation.
$ContactItemUI.innerHTML = `${selectedItem['fname']} <br> ${selectedItem['lname']} <br> ${selectedItem['phone']} <br> ${selectedItem['email']}`;
Let’s add some CSS code in the style.css file for the contact details module.
/* Contact Item Details Module */
#contact-item-details {
float: left;
width: 200px;
background: #333;
overflow: auto;
color: white;
padding: 10px;
margin-left: 1px;
}
At this stage, you will be able to see the details module data on the right when the li element is clicked. However, I want to show the first index content item on the details module on load.
To do that, I am going to invoke renderContactDetailsModule() method (with an argument 0 which would be the first contact item) inside init() method on the AddressBookView class.
this.renderContactDetailsModule(0);
li Active State
Let’s add a CSS class .active in the style.css file.
li:hover, .active {
background: rgba(0, 0, 0, 0.5);
}
As you can see, I have added .active class to an existing CSS rule separated by a comma.
Let’s go back to app.js and declare hightlightCurrentListItem() method inside AddressBookView class. This method will take a selectedIndex value as a parameter.
Then, get an array of li DOM elements using document.getElementByClassName and store them into $ContactListItems.
After that, loop through the DOM elements and remove the .active class from each li element if there is one.
Finally, add the .active class to the clicked li element which can be determined using selectedIndex value on the $ContactListItems DOM array.
hightlightCurrentListItem(selectedIndex) { const $ContactListItems = document.getElementsByClassName('contact-list-item'); for (let i = 0, len = $ContactListItems.length; i < len; i++) { $ContactListItems[i].classList.remove('active'); } $ContactListItems[selectedIndex].classList.add("active") }
Let’s invoke hightlightCurrentListItem() method inside renderContactDetailsModule() by passing selectedIndex varaible as an argument.
That’s it.
At this stage,
- The first list item from the Contact List Module is selected and has an active state as well.
- The first contact item data will be shown on the contact details module on the right.
- A contact list item can be clicked and everything should work as expected like in the screenshot.
Top comments (0)