Search code examples
reactjsscrollprogress-bare-commerce

Progress bar on scroll to the right - React - e-commerce product row


I'm trying to make a progress bar that starts as the user scrolls to the right for a row of products for a sample e-commerce site. I've been getting a number of type errors to which I've made adjustments, but I'm now still getting the error "TypeError: Cannot read property 'addEventListener' of null" for line 12.

Please could someone provide assistance so it will work? (fyi pardon this code, still a bit of a React newbie). Thanks!

import React from 'react';
import { taskData } from "./cdg-data";
import ProgressBar from './progress';

class CDG extends React.Component {
  state = {
    scrollPosition: 0
  }

  listenToScrollEvent = () => {
    const element = document.querySelector("CDG");
    element.addEventListener("scroll", () => {
      requestAnimationFrame(() => {
        element.calculateScrollDistance();
      });
    });
  }
  
  calculateScrollDistance = () => {
    const element = document.querySelector("CDG");
    const pixels = element.scrollLeft; // CURRENT number of pixels scrolled by the user

    const elementWidth = element.clientWidth; // width of just the element (no scrolling)
    const fullElementWidth = element.scrollWidth; // full total distance of the element (with scrolling)
  
    const totalScrollableDistance = fullElementWidth - elementWidth;
    const scrollPosition = Math.floor(pixels / totalScrollableDistance * 100) // gets the percentage that the user has scrolled
    
    element.setState({
      scrollPosition,
    })
  }

  componentDidMount() {
    this.listenToScrollEvent();
  }

  render() {
    return(
      <div>
    <h2 className="cdg-title">THE COLLECTION</h2>
      <div className="CDG">
        {taskData.map(item => {
          return (
            <div className="image-container">
              <a href={item.url} target="_blank" rel="noreferrer">
              <p className="title">{item.title}</p>
              <p className="price">{item.price}</p>
              </a>
                <img className="cdg-image" 
                  src={item.image} 
                  alt="Converse"
                />
            </div>
          );
        })}
      </div>
      <div className="Progress">
        <ProgressBar scroll={this.state.scrollPosition + '%'}/>
      </div>
      </div>
    );
  }
}



export default CDG;

Solution

  • This is the final correct solution if anyone needs it:

    import React from 'react';
    import { taskData } from "./cdg-data";
    import ProgressBar from './progress';
    
    class CDG extends React.Component {
      state = {
        scrollPosition: 0
      }
    
      listenToScrollEvent = () => {
        const element = document.querySelector(".CDG");
        element.addEventListener("scroll", () => {
          requestAnimationFrame(() => {
            this.calculateScrollDistance();
          });
        });
      }
      
      calculateScrollDistance = () => {
        const element = document.querySelector(".CDG");
        const pixels = element.scrollLeft; // CURRENT number of pixels scrolled by the user
    
        const elementWidth = element.clientWidth; // width of just the element (no scrolling)
        const fullElementWidth = element.scrollWidth; // full total distance of the element (with scrolling)
      
        const totalScrollableDistance = fullElementWidth - elementWidth;
        const scrollPosition = Math.floor(pixels / totalScrollableDistance * 100) // gets the percentage that the user has scrolled
        
        this.setState({
          scrollPosition,
        })
      }
    
      componentDidMount() {
        this.listenToScrollEvent();
      }
    
      render() {
        return(
          <div>
        <h2 className="cdg-title">THE COLLECTION</h2>
          <div className="CDG">
            {taskData.map(item => {
              return (
                <div className="image-container">
                  <a href={item.url} target="_blank" rel="noreferrer">
                  <p className="title">{item.title}</p>
                  <p className="price">{item.price}</p>
                  </a>
                    <img className="cdg-image" 
                      src={item.image} 
                      alt="Converse"
                    />
                </div>
              );
            })}
          </div>
          <div className="Progress">
            <ProgressBar scroll={this.state.scrollPosition + '%'}/>
          </div>
          </div>
        );
      }
    }
    
    
    
    export default CDG;
    import React from 'react';
    
    const ProgressBar = (props) => {
        const { scroll } = props;
    
        const containerStyles = {
            height: 5,
            width: '33%',
            backgroundColor: '#939191',
            margin: '0 auto 50px',
        }
    
        const fillerStyles = {
            height: '100%',
            width: `${scroll}`, // dependent on JavaScript scroll, not the percentage completed as in the example
            backgroundColor: '#A50209',
            textAlign: 'center'
        }
        return (
            <div style={containerStyles}>
                <div style={fillerStyles}>
                </div>
            </div>
        );
    };
    
    
    export default ProgressBar;