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

Using the useState Hook: What is the best way to add a new item to the end of an array stored in the state

I'm currently working on a project using Next.js and Tailwind, where I am trying to populate an array using the useState Hook. My intention is to iterate through the array using the map function and add items to the end of the array until there are n ...

Strategies for aligning tooltips with the locations of dragged elements

One of my projects involves a simple drag element example inspired by Angular documentation. The example features a button that can be dragged around within a container and comes with a tooltip. <div class="example-boundary"> <div ...

Transforming a string to a Date object using JavaScript

Can someone assist me in converting a PHP timestamp into a JavaScript Date() object? This is the PHP code I use to get the timestamp: $timestart = time(); I need help converting this timestamp into a JavaScript date object. The concept of working with d ...

Toggling display of divs with jQuery using plus and minus icons

Is it possible to use two different icons as a sprite image for when the show and hide functions are active on a div element? For instance, having a + icon displayed when showing the div, and then switching to a - icon when hiding it. The current code I ...

What is the best way to remove <a> tags from an XML document?

I'm dealing with <a> tags within an XML file and I need to remove only the <a> tags. For instance: <test>This is a line with <a name="idp173968"></a> tags in it</test> I am unable to use str_replace to replace t ...

Utilizing nodejs to interact with a web service

Recently diving into Node.js and currently exploring how to utilize services with NodeJS. Seeking guidance on the NodeJS equivalent of the code snippet provided below: $.ajax({ type: "POST", url: "/WebServiceUtility.aspx/CustomOrderService", data: " ...

Exploring JSON information containing colons in the labels

I am having trouble accessing JSON data in Angular that contains a colon in the label (refer to responsedata). This is the code causing issues: <li ng-repeat="i in items.autnresponse.responsedata | searchFor:searchString"> <p>{{i.autn:numhits} ...

What is the best way to generate multiple progress bars by leveraging a for loop?

Exploring the code snippet below, I've been creating a progress bar that reacts to specific data input in array form. However, the issue I've encountered is that the code only generates a single progress bar. How can I incorporate this into my f ...

What is the process for exporting all sub-module types into a custom namespace?

When we import all types from a module using a custom-namespace, it appears to work smoothly, for example: import * as MyCustomNamespace from './my-sub-module' We are also able to export all types from a module without creating a new namespace, ...

What is the process for extracting the background color from a typescript file in Angular and connecting it to my HTML document?

My typescript array population is not changing the background color of my div based on the values in the array. I've attempted to set the background using [style.backgroundColor]="statusColor[i]", where statusColor is an array declared in my typescrip ...

No server needed reCAPTCHA

I have successfully integrated reCAPTCHA into a Contact form on my website, which includes three fields: name, email, and message. The data from this form is stored in Firebase for easy access. This implementation was done using React, with the help of th ...

Is the AngularJS Date property model sending an incorrect value to the server?

There are some puzzling things I am trying to figure out. When using datetimepicker, the Date and time selected appear correctly on the screenshot. The value in the textbox is accurate The model's value in console is correct (but hold on a second... ...

Tips for creating a stylish navbar with a faded effect on the right side and integrating a fresh button into its design

I'm looking to enhance my Navbar by implementing a feature where, if the width of the Navbar exceeds that of its parent div, it will fade on the right side and a new button will be added - similar to what can be seen on Youtube. 1- When the Navbar&ap ...

Unconventional way of assigning class properties in Typescript (Javascript): '?='

Recently, I came across the ?= assignment expression within a class property declaration. Can anyone provide some insight into what this means? I am familiar with the new Optional Chaining feature (object?.prop), but this particular syntax is unfamiliar t ...

What could be the reason behind the issue of my HTML draggable feature not functioning properly on touch

I've set up a draggable div with headers and p tags, but I'm encountering an issue when trying to run it on a touch screen. The div tag isn't draggable in this scenario. Can anyone suggest the best solution for making this page touch screen ...

Tips on customizing the CSS responsiveness of your Bootstrap carousel

I have recently installed a Bootstrap theme on my Wordpress site and I am trying to make some CSS adjustments. One issue I am facing is with the carousel. Currently, when I resize the website or view it on a mobile device... The carousel maintains a lar ...

I am facing an issue with properly linking my jQuery

After searching through numerous posts on this website, I have yet to find a solution to my problem. My issue involves trying to implement a simple jQuery function that is not functioning as expected. It appears that the jQuery link may not be properly set ...

What is the method for including a TabIndex property in a JSON file?

https://i.sstatic.net/ISi72.png I discussed the integration of HTML fields within a JSON file and highlighted how to utilize the TabIndex property effectively in JSON files. ...

Animating Array of Paragraphs with JQuery: Step-by-Step Guide to Displaying Paragraph Tags Sequentially on Each Click

var phrases = ['phraseone', 'yet another phrase', 'once more with feeling']; $(".btn").on('click', function() { for(var i=0; i < phrases.length; i++) { container.innerHTML += '<p>' + ...

Issue encountered while loading JSON data into DynamoDB's local instance

I have successfully set up DynamoDB local and everything is functioning as expected after going through their documentation. I have also tested their example code, which worked flawlessly. The Users table has been created with the name "Users". Below is ...