React JS Slideshow | Responsive, Autoplay with Manual Navigation Buttons
Tags: programming, react, javascript, webdev
Published: 2021-01-07 15:58:21

### <center>React JS Slideshow | Responsive, Autoplay with Manual Navigation Buttons</center>

------------

#### <center>Browse our [Teachable](http://anobject.teachable.com "Teachable") courses.</center>

------------

![ReactJS Slideshow](https://user-images.githubusercontent.com/58220766/103496265-9b2daa00-4e0b-11eb-8c74-41ce584e9254.png)

### We'll start by putting all of our elements on screen.

We need two components: **Deck.js** and **Card.js**

We have 6 elements to work with:
- In the **Deck.js**
1. an **img** that we'll use as a button for moving right (IMG)
2. an **img** that we'll use as a button for moving left (IMG)
3. a **view port** (DIV)
4. an **images container** (DIV)
5. a **DIV** to hold our "radio" buttons (DIV)

- In the **Card.js**
6. a **"card"** which we'll use to hold the image (DIV)
7. the actual **image** (IMG)

![elements on screen no css](https://user-images.githubusercontent.com/58220766/103497568-f19ce780-4e0f-11eb-950b-ea80edf5ecdc.png)

#### Now we need to center everything on screen.

We'll use a combination of **absolute positions** and **transforms**.

![elements on screen centered](https://user-images.githubusercontent.com/58220766/103497802-9d463780-4e10-11eb-9cdd-1a7a042139a5.png)

#### Now that everything is centered, we need to order the cards from left to right.

We write a simple function that determines the **middle card** of our deck and moves the cards to the left of that middle to the left and the cards to the right of that middle to the right.

```
order_cards = () => {
    // const card_width = parseFloat(getComputedStyle(this.images.children[0]).width);
    let counter_for_right = 1,
        counter_for_left = this.middle_card_by_index;

    for (let i = 0; i < this.images.children.length; i++) {
        this.images.children[i].style.transitionDuration = '0.0s';

        if (i < this.middle_card_by_index) {
            this.images.children[i].style.left = `-${(counter_for_left * this.new_width) - (this.new_width / 2)}px`;
            counter_for_left--;
        } else if (i > this.middle_card_by_index) {
            this.images.children[i].style.left = `${(counter_for_right * this.new_width) + (this.new_width / 2)}px`;
            counter_for_right++;
        } else {
            this.images.children[i].style.left = `${this.new_width / 2}px`;
        }
    }
}
```

![elements on screen ordered](https://user-images.githubusercontent.com/58220766/103497998-442ad380-4e11-11eb-8f08-89d3c697af7b.png)

------------

### Let's discuss RESPONSIVE.

To make our carousel responsive, we need to resize our **view port** based on **percentages** and not hard pixel units.

So we'll just pick **50%** and calculate a hard **pixel** count based on the width of the user's browser window; then we'll run this code in a **resize window event listener**.

```
window.addEventListener('resize', () => {
    img_width_as_percentage = 50;
    // img_width_as_percentage = window.innerWidth < 768 ? 100 : img_width_as_percentage;

    this.new_width =
        /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent) ?
            (img_width_as_percentage / 100) * window.screen.width :
            (img_width_as_percentage / 100) * window.innerWidth;

    this.view_port.style.width = `${this.new_width}px`;

    this.order_cards();
});
```

**We also resize our left & right buttons and "radio" buttons based on the viewport and window width**.

##### Now that we have our **resize** code, every time we resize the browser window, our carousel resizes.
![elements on screen responsive](https://user-images.githubusercontent.com/58220766/103498293-39247300-4e12-11eb-86c8-b03cac695ed5.png)

------------

### We have 3 large tasks on-hand.

1. BUTTON navigation
2. "RADIO" navigation
4. Auto-Play functionality

#### The basis for ALL navigation is this piece of code here:

```
for (let i = 0; i < this.images.children.length; i++) {
    this.images.children[i].style.transitionDuration = '0.0s';

    const updated_position = this.last_positions[i] + /* some difference */;

    this.images.children[i].style.left = `${updated_position}px`;
    this.last_positions[i] = updated_position;
}
```

##### What it says is this:
Take the current position of all of the images...

> where are they on the X-axis?

...add some **difference** which will move them to a new position.

We have a **last_positions** array to keep track of where our images are on screen.

-----------------

### Let's code up the BUTTON navigation.

Very simple.

We move the images left or right **one whole card width**.
This makes our **difference** simply equal to the width of one card.

```
this.images.children[i].style.transitionDuration = '0.0s';

const updated_position = this.last_positions[i] + this.new_width;

this.images.children[i].style.left = `${updated_position}px`;
this.last_positions[i] = updated_position;
```

![button-nav](https://user-images.githubusercontent.com/58220766/103499117-f7e19280-4e14-11eb-9deb-120cc84601a2.gif)

------------

### Let's code up the RADIO navigation.

Very simple.

We calculate which "radio" button the user clicked.

Do a little calculation to determine how many pixels to move left or right.
That is our **difference**.

```
let new_card = null;

for (let i = 0; i < this.images.children.length; i++) {
    if (event.target === this.selection_buttons_container.children[i]) new_card = i;
}

for (let i = 0; i < this.images.children.length; i++) {
    const updated_position = this.last_positions[i] + ((this.current_card - new_card) * this.new_width);

    this.images.children[i].style.transitionDuration = '0.0s';
    this.images.children[i].style.left = `${updated_position}px`;
    this.last_positions[i] = updated_position;
}
```

![radio-nav](https://user-images.githubusercontent.com/58220766/103499410-f5cc0380-4e15-11eb-9733-390a3537f543.gif)

------------

### Let's handle Auto-Play.

#### The logic is simple.

Just put our **next** button navigation logic in a **setInterval()** function.

```
this.autoplay_timeout_id = setTimeout(() => {
    this.autoplay_interval_id = setInterval(() => {
        for (let i = 0; i < this.images.children.length; i++) {
            this.images.children[i].style.transitionDuration = '0.0s';

            const updated_position = this.last_positions[i] - this.new_width;

            this.images.children[i].style.left = `${updated_position}px`;
            this.last_positions[i] = updated_position;
        }

        this.current_card = (this.current_card === this.number_of_cards_by_index) ? 0 : ++this.current_card;
    }, 1100);
}, 1200);
```

![rjs-auto-finished](https://user-images.githubusercontent.com/58220766/103838578-6a43b400-505b-11eb-853e-cb958026d8d0.gif)

------------

### There is much more nuance to this project.

You can get the source files [here](https://github.com/an-object-is-a/reactjs-autoplay-slideshow "here") and you can follow the video tutorial down below.

------------

#### <center>If you want a more in-depth guide, check out my full video tutorial on YouTube, **[An Object Is A](https://www.youtube.com/c/anobjectisa "An Object Is A")**.</center>

### <center>React JS Slideshow | Responsive, Autoplay with Manual Navigation Buttons</center>

{% youtube On4yoJcUKG8 %}