- Download the example or clone the repo
- Open
index.html
document and Go Live (Live Server extension on VSCode). - Ready to use!
movie-to-watch
display movies playing in the theaters, that are fetched from themoviedb.org API in a friendly way. Users are able to browse through the latest movies using inifinite scrolling without waiting any new HTTP GET request, to render content.
Every movie is clickable where the <div>
content extends in an performant way, where details like trailer, reviews, similar movies are presented (if they exist).
There is also added an <input>
field where user can search for specific movies.
-
In the below GIF can see the infinity scroll functionality and the expand option with the relevant results of each movie.
-
In the below GIF you can also see the search option of a movie and the infinity scroll / expand option of the results.
- Vanilla JavaScript (Web Components)
- HTML5
- CSS
The app follows the component based architecture and consists of five main parts.
appModule
is the main file which contains the definition of the base component (<app-root>
) and the rest of the components that are used (listItem, reviewItem, similarItem
).appComponent
is the file where the container of the web application is first created. The most important parts that are applied are the search<input>
and the reusablelistItem
component which is wrapped with the "infinity scrolling" functinality.reviewItem, similarItem
components that works the same likelistItem
, which means that are all reusable.movieService
where the communication with the API and all the HTTP GET requests of the application are applied.utils
which is a generic file where scrolling process gets observed / unobserved in different files of the application.
The appModule
contains the definition of the defineElements()
method where all the components present in the application are defined there.
export class AppModule {
static defineElements() {
window.customElements.define("app-root", AppComponent);
window.customElements.define("list-item", ListItem);
window.customElements.define("review-item", ReviewItem);
window.customElements.define("similar-item", SimilarItem);
}
}
The appComponent
forms the main body of the application, which contains the basic code which is required. Furthermore appComponent
is the only component exposed in the index.html
as <app-root>
.
In the below example template element is created and then in the HTML list-items
are rendered as a <div>
tag which is rendered as an external reusable component.
const appTemplate = document.createElement("template");
appTemplate.innerHTML = `
<style>
@import url('./src/appComponent.css')
</style>
<div class="list-container">
<span class="title" >Latest Movies</span>
<form class="search-container">
<input type="text" class="search-input" name="movieName" placeholder="Search">
</input>
<input type="submit" value="Search" class="search-btn"></input>
</form>
<div class="scroller">
<div class="list-items"></div>
<div class="sentinel"></div>
</div>
</div>
`;
After the creation of HTML, getNewestMovies()
(HTTP GET) call returns the newest movies which are mapped and a list-item
created with the createElement("list-item)
.
renderMovies(movies) {
.
.
.
movies.map((movie) => {
let listItem = document.createElement("list-item");
listItem.id = movie.id;
listItem.movie = movie;
this.listItems.appendChild(listItem);
});
this.listItems.appendChild(this.sentinel);
}
For the infinity scrolling process a <div>
named sentinel
added exactly after the listItem, reviewItem, similarItem
. Then the IntersectionObserver
observe when the sentinel
gets rendered and then a new HTTP GET request applied.
The observe / unobserve happens in the same way in three different places so a generic file with the name utils
hosting functions for both cases.
In the setUpScrollListeners
gets passed arguments like self
just to inform function where should look up for "this". sentinel
that when gets rendered entry.interesctionRatio
becomes 1 so the corresponding callback applied based on the process and params where the search value used for scrolling cases like a movie search.
export const setUpScrollListeners = (self, sentinel, callback, params) => {
let intersectionObserver = new IntersectionObserver((entries) => {
if (entries.some((entry) => entry.intersectionRatio > 0)) {
callback.call(self, params);
}
});
intersectionObserver.observe(sentinel);
return intersectionObserver;
};
tearDownScrollListeners
similarly gets intersectionObserver
from setUpScrollListeners
and sentinel
, too. Then sentinel just gets unobserved just to be able setUpScrollListeners
applied again in different usage after the already rendered results gets cleared.
export const tearDownScrollListeners = (intersectionObserver, sentinel) => {
intersectionObserver.unobserve(sentinel);
};
tearDownScrollListeners
is called in cases like when tear down and return zero results.
- Unit tests
- Mobile friendly
- Error handling
- Empty state
- Memory cache
- To-Do List Application (Web Components)
- MDN Web Components
- webcomponents.org
- webcomponent.dev
- Using Fetch method
- MDN Intersection Observer
- Component-based Architecture for Dynamic HTML Websites
- Adding State to Custom HTML Elements
- web-components-examples
- How To Do Infinite Scrolling the Right Way
- IntersectionObserver Sample
- w3schools How TO - Collapse