Using Javascript to dynamically swap images within a div as the user scrolls

Can someone help me achieve a similar image scrolling effect like the one on the left side of this website:

I am looking to change the image inside a div as I scroll and ensure that the page doesn't scroll further until all the images have been scrolled through.

I want to learn JavaScript basics before moving on to jQuery, so I need assistance using pure JS.`

window.onscroll = function() {
    console.log(window.pageYOffset);
    var img1 = document.getElementById('img1');
    var img2 = document.getElemebtById('img2')
    if ( window.pageYOffset > 1000 ) {
        img1.classList.add("hidden");
        img2.classList.remove("hidden");
    } else {
        img2.classList.add("hidden");
        img1.classList.remove("hidden");
    }
}
.rightPhotos {
    max-width: 50%;
    height: 50%;
    overflow: auto;
}

.aPhoto {
    max-height: 100%;
}

.hidden {
    visibility: hidden;
}

.images {
    width: 100%;
    height: 100%;
}
      <div class="other">
          <div class="rightPhotos" onscroll="myFunction()">
              <div class="aPhoto">
                  <img class ="images" id="img1" src="IMAGES/sunglasses.jpeg"  alt="Woman with Sunglasses">
              </div>
              <div class="aPhoto hidden">
                  <img class="images" src="IMAGES/dancer1.jpg" alt="A Dancer">
              </div>
          </div>
      </div>

`

Answer №1

The link you provided showcases a beautiful page design, prompting me to take the time to create something that closely resembles it in comparison to other responses.

  • I implemented a functioning transition similar to that on franshalsmuseum.nl.
  • I ensured that the page is responsive to resizing:
    • All panes and images adjust relative to each other,
    • Scroll steps are in relation to page height,
    • Images utilize <div> with background-image instead of the <img> tag. They are slightly cropped based on window size changes to accommodate varying aspect ratios.
  • I made changing the number of image sets simple. While it could be enhanced by creating HTML elements in JavaScript, this isn't necessary for the original page.

Functionality Overview

HTML

Images are enclosed in special wrapper elements (.img-wrapper) to handle appropriate sizing and positioning (notably position: relative). Each image element is assigned a URL (as background image) and an image set number for use in JavaScript:

<div class="img visible" data-imageset="1"
    style="background-image: url('http://placeimg.com/640/480/people');">
</div>

The class visible displays image set 1 initially.

CSS

Key definitions include these (and similar ones for #bottom-image). The image container having overflow: hidden allows us to hide the image by moving it out of viewable area. Setting the coordinates back triggers a smooth transition effect causing the image to slide back into place.

/* hiding images in #top-image */
#left-pane #top-image .img {
  top: 100%;
}
#left-pane #top-image .img.visible {
  top: 0;
}

JS

The JavaScript code is minimal with just one interaction line with the DOM. It utilizes some clever tricks which may not be immediately evident, hence the inclusion of links to relevant documentation:

document.querySelectorAll('#left-pane .img').forEach((img) => {
    img.classList.toggle('visible', img.dataset.imageset <= currentSet);
}

This script locates all images and toggles the class visible based on the image's data-imageset attribute.

Complete Code Snippet with Demo

Refer to the snippet below. For optimal viewing, click the "Full page" option after running the snippet.

let currentSet = 1;

function updateSelectedImgSet() {
  const currentScroll = document.scrollingElement.scrollTop;
  const scrollMax = document.scrollingElement.scrollHeight - document.scrollingElement.clientHeight;
  const setsCount = 3;
  const scrollPerSet = scrollMax / setsCount;
  const scrolledSet = Math.floor(currentScroll / scrollPerSet) + 1;
  if (scrolledSet == currentSet) {
    return;
  }
  currentSet = scrolledSet;
  document.querySelectorAll('#left-pane .img').forEach((img) => {
    img.classList.toggle('visible', img.dataset.imageset <= currentSet);
  });
}

window.onscroll = updateSelectedImgSet;
window.onresize = updateSelectedImgSet;
/* Left pane, fixed */
#left-pane {
  position: fixed;
  height: 100%;
  width: 40vw;
}

#left-pane .img-wrapper {
  position: relative;
  height: 50%;
  width: 100%;
  overflow: hidden;
}

#left-pane .img-wrapper .img {
  position: absolute;
  width: 100%;
  height: 100%;
  /* Sizing and cropping of image */
  background-position: center;
  background-size: cover;
  /* Transition - the slow sliding of images */
  transition: 0.5s all ease-in-out;
}

/* hiding images in #top-image */
#left-pane #top-image .img {
  top: 100%;
}
#left-pane #top-image .img.visible {
  top: 0;
}

/* hiding images in #bottom-image */
#left-pane #bottom-image .img {
  bottom: 100%;
}
#left-pane #bottom-image .img.visible {
  bottom: 0;
}

/* Right pane, scrolling with the page */

#right-pane {
  margin-left: 40vw;
}

.scrollable-content {
  font-size: 40vw;
  writing-mode: vertical-rl;
  white-space: nowrap;
}
<div id="left-pane">
  <div id="top-image" class="img-wrapper">
    <div class="img visible" data-imageset="1"
        style="background-image: url('http://placeimg.com/640/480/people');">
    </div>
    <div class="img" data-imageset="2"
        style="background-image: url('http://placeimg.com/640/480/animals');">
    </div>
    <div class="img" data-imageset="3"
        style="background-image: url('http://placeimg.com/640/480/any');">
    </div>
  </div>
  <div id="bottom-image" class="img-wrapper">
    <div class="img visible" data-imageset="1"
        style="background-image: url('http://placeimg.com/640/480/nature');">
    </div>
    <div class="img" data-imageset="2"
        style="background-image: url('http://placeimg.com/640/480/tech');">
    </div>
    <div class="img" data-imageset="3"
        style="background-image: url('http://placeimg.com/640/480/arch');">
    </div>
  </div>
</div>
<div id="right-pane">
  <div class="scrollable-content">Scrollable content!</div>
</div>

Answer №2

Check out the code snippet below:

(I changed 1000 to 60 in the function for visualization purposes)


I am utilizing one image and using onscroll to switch the src of the image.

window.onscroll = function() {
    var img1 = document.getElementById('img1');
    var img2 = document.getElementById('img2')
    if ( window.pageYOffset > 60 ) {
 document.getElementById("img1").src = "https://material.angular.io/assets/img/examples/shiba2.jpg";
    } else {
 document.getElementById("img1").src = "https://material.angular.io/assets/img/examples/shiba1.jpg";
    }
}
.rightPhotos
{
    max-width: 50%;
    height:50%;

    overflow: auto;
}
.aPhoto
{

    max-height: 100%;


}



.images
{
    width: 100%;
    height: 500px;
}
      <div class="other">
         <div class="rightPhotos" onscroll="myFunction()">
           <div class="aPhoto">
               <img class ="images" id="img1" src="https://material.angular.io/assets/img/examples/shiba1.jpg"  alt="Woman with Sunglasses"/>
   </div>

  </div>

 </div>

Answer №3

To show or hide elements using CSS properties is a more efficient method than creating custom CSS with a hidden class.

if ( window.pageYOffset > 1000 ) {
        img1.style.visibility = 'hidden';  
        img2.style.visibility = 'visible';  
    } else {
        img2.style.visibility = 'hidden';  
        img1.style.visibility = 'visible';  
}

Although the above code hides the element, it still occupies space within the DOM.

If you want to completely remove the space occupied by the element:

if ( window.pageYOffset > 1000 ) {
        img1.style.display = 'none';
        img2.style.display = 'block';
    } else {
        img1.style.display = 'block';
        img2.style.display = 'none'; 
}

Answer №4

//window.pageYOffset

var scrollingDiv = document.getElementById('scrollContainer');
var img1 = document.getElementById('img1');
var img2 = document.getElementById('img2');

  scrollingDiv.onscroll = function(event) {

  if (scrollingDiv.scrollTop < 500) {
    img1.src = "https://placeimg.com/250/100/arch";
    img2.src = "https://placeimg.com/250/100/animals";
  }
  
  if (scrollingDiv.scrollTop > 500) {
    img1.src = "https://placeimg.com/250/100/nature";
    img2.src = "https://placeimg.com/250/100/people";
  }
  if (scrollingDiv.scrollTop > 1000) {
    img1.src = "https://placeimg.com/250/100/tech";
    img2.src = "https://placeimg.com/250/100/any";
  }
 
 
.container{
  display: table;
  width: 100%;
  height: 100%;
 }
 body{
 margin: 0;
 }
.container > div {
  vertical-align:top;
 }
 .left, .middle, .right {
   display: table-cell;
   height: 100vh;
   box-sizing: border-box;
   
 }
 .left, .right{
   width:40%;
   background: gray;
 }
 
 .middle{
   overflow: auto;
   position: relative;
 }
 
 .in-middle{
   background: tomato;
    position: absolute;
    width: 100%;
    height: 100%;
    box-sizing: border-box;
    margin: 0;
 }
 .in-in-middle{
   height: 500px;
   background: tomato;
 }
 
 .in-in-middle:nth-child(2){
  background: pink;
 }
 
 .in-in-middle:nth-child(3){
  background: skyblue;
 }
 
 .left img{
  width: 100%;
  transition: all 0.5s;
 }
<div class="container">
  <div class="left">
    <img id="img1" src="https://placeimg.com/250/100/arch">
    <img id="img2" src="https://placeimg.com/250/100/animals">
  </div>
  <div class="middle" id="scrollContainer">
    <div class="in-middle">
      <div class="in-in-middle" id="1"></div>
      <div class="in-in-middle" id="2"></div>
      <div class="in-in-middle" id="3"></div>
    </div>
  </div>
</div>

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Set the Checkbox selections by utilizing the array values in the MUI DataGrid

In my MUI Datagrid, I have 2 columns for ID and City Name. There are only 3 cities represented in the table. Within a citizen object like this: const userMockup = { id: 1, name: "Citizen 1", cities: [1, 2], isAlive: true }; The cities ar ...

CSS regression testing through version control systems and continuous integration

Is there a tool that integrates with CI systems and VCS like Git to automatically assign regression test results to individual commits? Ideally, this tool would provide a concise summary on a main page showing passed/failed numbers for each commit, with th ...

Select box failing to display default value

I am dealing with a specific data structure: $scope.personalityFields.traveller_type = [ {"id":1,"value":"Rude", "color":"red"}, {"id":2,"value":"Cordial", "color":"yellow"}, {"id":3,"value":"Very Friendly", "color":"green"}, ]; Also, there is a se ...

Bordering the isotopes

Currently, I am utilizing a plugin that involves the isotope filter library. By setting the width to 33.33%, my list elements are organized into 3 columns. I am trying to specify the middle column to have side borders. However, whenever I click on the filt ...

What steps should be taken to retrieve the contents of a file that has been chosen using the browse

You have successfully implemented a browse button that allows the user to navigate the directory and choose a file. The path and file name are then displayed in the text element of the Browse button complex. Now, the question arises - how can I extract dat ...

Controller function failing to trigger

I'm new to asking questions, so if I've made a mistake, please let me know. I've searched for an answer here but couldn't find one. Any help would be greatly appreciated. I'm attempting to load a list of "Countries" using a contro ...

React has been reported to display a Minified React error even in its development mode

Currently, I am utilizing browserify and babel for transpiling and bundling my script. However, a challenge arises when React 16 is incorporated as it presents the following error message: Uncaught Error: Minified React error #200; for more details, kin ...

Show the subscription response data in Angular

When utilizing the code snippets below from two different components, I am able to receive a valid response value from the subscriber. dataService.ts fetchFormData(){ return this.http.get('http://localhost:48116/RecuruitmentService.asmx/addRoleTest ...

"Step-by-step guide on uploading multiple images to a Node server and storing them in

Hey everyone! I'm currently working on a project using React and MongoDB. Users are required to register and login before accessing the app. Once logged in, they can input their name, number, and images through a form. However, I've encountered a ...

What tools do Sketchfab and Thangs utilize to display 3D models?

My website functions as a digital library for a particular niche of 3D models. However, I've noticed that the performance on mobile devices when using modelviewer to display glb files is quite poor. Frequently, the page crashes and reloads unexpectedl ...

Maintaining active navigation state in JQuery/JavaScript after clicking a link: tips and tricks

While many resources discuss adding the active class to a nav link using jquery, there is less information on maintaining the active state after the nav link has been clicked. After experimenting with code from various sources, I have attempted to set ses ...

Filtering Sails.js query model based on a collection attribute

Imagine I have these models set up in Sails.js v0.10: Person.js module.exports = { attributes: { name: 'string', books: { collection: 'book', via: 'person' } } }; Book.js module.exports = { ...

Is there an alternative to the jQuery :contains selector?

How can I choose an option from a drop-down menu based on its text value? When I execute the following code: var desiredText = "Large"; $("#size option:contains(" + desiredText + ")").attr('selected', 'selected'); it ends up selecting ...

Having trouble retrieving a value from the $http promise, causing the code within the then() function to not run as expected

In the past, I encountered a challenge with the $http service which I managed to solve by creating a dedicated service for handling my requests. However, as my requests grew larger, this solution started to seem inefficient. Instead of just assigning a sim ...

Add fresh material to the bottom of the page using Javascript

Hey there, I'm having a bit of trouble with my page where users can post their status. I want the new posts to appear at the bottom after the older posts when the user presses the button. Currently, Ajax is placing all new posts at the top of the old ...

Ways to implement CSS styling for a particular HTML element

HTML: <div class="tagline">Web Portal Name</div><br /> <div id="logged_in_user_dtls" style="color:#FFF; margin-top:15px; font:bold; width:100%; position:relative; margin-left:800px;float:left;"&g ...

The count of bits is not producing the anticipated result

Attempting to tackle the challenge of Counting Bits using JavaScript, which involves determining the number of set bits for all numbers from 0 to N, storing them in an array, and returning the result Let me provide an explanation Input: n = 5 ...

What is the best way to incorporate polling into the code provided below? I specifically need to retrieve data from the given URL every 60 seconds. How should I go about achieving this

Can you assist me in integrating polling into the code below? <!DOCTYPE html> <html> <head> <script src="https://code.jquery.com/jquery-3.1.1.min.js"></script> <script src="https://code.highcharts.com/highcharts.js"> ...

Encountered an issue with launching Chrome in Puppeteer for Node.js

My code was uploaded to EC2 AWS, but unfortunately it is not working properly after the upload. Interestingly, it runs correctly on localhost. Despite trying various solutions, I am still facing this issue. Any suggestions on the correct approach to resolv ...

Adding text chips to a text field in Vuetify - A simple guide

I have successfully integrated a text field with vuetify and now I am looking to incorporate chips into it. Currently, chips are only added if the entered text matches a specific pattern (such as starting with '{' and ending with '}'). ...