Adjusting the color of an element in ReactJS as the user scrolls and hits a specific position

Is there a way to dynamically change the color of my header based on the background color as I scroll through different sections of my webpage? I have a fixed header and multiple sections with varying background colors. I want the header's text color to change for better readability when it overlaps with a section of a different background color. However, I'm unsure of how to achieve this effect. I've looked online for solutions but haven't found anything that addresses this specific issue.

Here's what I have tried so far: (check out this JSFIDDLE)


class Div extends React.Component{
   constructor() {
     super()

     this.state = {
       headerClass: 'white'
     }
   }
   changeColor() {
     // something like
     this.setState({ headerClass: 'black'})
   }
   render(){
     return(
       <div>
         <div id="header">
           <h1 className={`${this.state.headerClass}`}>
             This is the header
           </h1>
         </div>      
         <div id="section_1" className="section">
           This is section 1
         </div>

         <div id="section_2" className="section">
           This is section 2
         </div>

         <div id="section_3" className="section">
           This is section 3
         </div>

         <div id="section_4" className="section">
           This is section 4
         </div>

         <div id="section_5" className="section">
           This is section 5
         </div>
       </div>
     )
   }
}

Here's the CSS:


#main {
  height: 2000px;
  position: relative;
}

#section_1 {
  background: grey;
}

.section {
  height: 400px;
  background: white;
  padding: 30px 0;
}

#header {
  height: 50px;
  background: transparent;
  position: fixed;
  width: 100%;
  left: 0;
  top: 0;
  right: 0;
  z-index: 1
}

h1 {
  color: white;
}

Any suggestions or hints on how to achieve this effect?

Answer №1

Check out this solution:

import React from 'react'

export default class Div extends React.Component{
  state = {
    color: 'white'
  }

  listenScrollEvent = e => {
    if (window.scrollY > 400) {
      this.setState({color: 'black'})
    } else {
      this.setState({color: 'white'})
    }
  }

  componentDidMount() {
    window.addEventListener('scroll', this.listenScrollEvent)
  }

  render() {
    return(
      <div>
        <div id="header">
          <h1 style={{color: this.state.color}}>
            This is the header
          </h1>
       </div>
       <div id="section_1" className="section">
          This is section 1
       </div>

       <div id="section_2" className="section">
          This is section 2
       </div>

       <div id="section_3" className="section">
          This is section 3
       </div>

       <div id="section_4" className="section">
          This is section 4
       </div>

       <div id="section_5" className="section">
          This is section 5
       </div>

     </div>
     )
   }
}

By using window.scrollY, we track the user's scrolling position to update the color accordingly.

Answer №2

To implement this functionality with React Hooks, you can follow the example provided below.

If you want to see the complete code and files related to this implementation, you can check out the Code SandBox. The Code SandBox contains 'body.js' and 'styles.css' files that demonstrate changes on scroll. Additionally, below is the Header Component along with its corresponding CSS file. Feel free to explore the Code SandBox for a better understanding: (https://codesandbox.io/s/header-change-color-onscrolly-2z3vt)

// Header Component

import React, { useState, useEffect } from 'react'
import "./Header.css"

function Header() {
  const [header, setHeader] = useState("header")

  const listenScrollEvent = () => {
    if (window.scrollY < 73) {
      return setHeader("header")
    } else if (window.scrollY > 70) {
      return setHeader("header2")
    }
  }

  useEffect(() => {
    window.addEventListener('scroll', listenScrollEvent);

    return () => {
      window.removeEventListener('scroll', listenScrollEvent);
    }
  }, []);

  return (
    <header className={header}>
      <div className="logo">Logo</div>
      <ul className="links">
        <li className="link-item">home</li>
        <li className="link-item">about</li>
        <li className="link-item">join</li>
      </ul>
    </header>
  );
}

export default Header;

CSS file:

// Styles header.css

.header {
  position: absolute;
  top: 0;
  left: 0;
  display: flex;
  align-items: center;
  justify-content: space-between;
  width: 100%;
  height: 120px;
  background-color: #fff;
  color: #333;
  transform: translateY(0);
  transition: transform 0.6s ease;
}

.header2 {
  position: fixed;
  top: 0;
  left: 0;
  display: flex;
  align-items: center;
  justify-content: space-between;
  width: 100%;
  height: 86px;
  background-color: gray;
  color: rosybrown;
  transform: translateY(10);
  transition: transform 6s ease;
  animation: myanimation 3s;
}
@keyframes myanimation {
  0% {
    background-color: transparent;
    color: transparent;
  }
  35% {
    background-color: gray;
    color: rosybrown;
  }
  100% {
    background-color: gray;
    color: rosybrown;
  }
}

.logo {
  margin: 0 24px;
  font-size: 28px;
  color: #f59596;
}

.links {
  padding: 0;
  margin: 0 24px;
}

.link-item {
  display: inline-block;
  margin: 0 12px;
  cursor: pointer;
}

Answer №3

A new approach to using hooks in React 16

import React from 'react'

export default Div => (){
  const [headerColor, setHeaderColor] = useState("white")


const updateHeaderColor = () => {
    window.scrollY > 10
      ? setHeaderColor("black")
      : setHeaderColor("white")
  }
// Mimicking the functionality of componentDidMount and componentDidUpdate:
useEffect(() => {
  window.addEventListener("scroll", updateHeaderColor)
})

render() {

return(
  <div>
    <div id="header">
      <h1 style={{color: headerColor}}>
        Changing the header color dynamically
      </h1>
   </div>
   <div id="section_1" className="section">
      This is section 1
   </div>

   <div id="section_2" className="section">
      This is section 2
   </div>

   <div id="section_3" className="section">
      This is section 3
   </div>

   <div id="section_4" className="section">
      This is section 4
   </div>

   <div id="section_5" className="section">
      This is section 5
   </div>

 </div>
 )
 }
}

Answer №4

To enhance user experience, consider creating individual components for each section that maintain a reference to their corresponding DOM element. By implementing a Section component that listens for scroll events and triggers a callback when its DOM element intersects with a fixed header, you can achieve a smoother scrolling effect.

Here is an example of how your Section component could be structured:

class Section extends React.Component {

    ref = node => {
        this.ref = node;
        window.addEventListener('scroll', this.onScroll);
    }

    componentWillUnmount() {
        window.removeEventListener('scroll', this.onScroll);
    }

    onScroll = event => {
        const {changeHeaderColor} = this.props;
        // determine if the top position is within the header by referencing the DOM element
        if (...) {
            changeHeaderColor();
        }
    }

    render() {
        const {id, children} = this.props;

        return (
            <div id={id} className="section" ref={this.ref}>{children}</div>
        );
    }
}

You can then easily render your sections like this:

<Section id="section-1" changeHeaderColor={this.changeColor}> content here </Section>

Answer №5

Here is my approach:

First, I set up a function to be triggered on scroll within the componentDidMount method:

window.addEventListener("scroll", this.handleScroll, false);

Next, I define the function and create a reference to it in the constructor:

Constructor:

    this.handleScroll = this.scrollFunction.bind(this);

Finally, I add the necessary logic inside the function:

scrollFunction() {
  // Add your logic and checks here.
}

Answer №6

Give this a shot: `import React, { useState } from 'react'; import './Navbar.css';

const NavBar = () => {

const [navbar, setNavbar] = useState(false);
const [TextHome,setTextHome]=useState(false);
const [TextAbout,setTextAbout]=useState(false);
const [TextService,setTextService]=useState(false);
const [TextPortfolio,setTextPortfolio]=useState(false);
const [TextContact,setTextContact]=useState(false);


const changeBackground = () => {
    if (window.scrollY >= 200) {
        console.log(window.scrollY);
        setNavbar(true);
        setTextHome(true);
         if (window.scrollY >= 600 && window.scrollY <= 920 ) {
            setTextAbout(true);
            setTextService(false);
            setTextPortfolio(false);
            setTextContact(false);




        }
        else if (window.scrollY >=921 && window.scrollY <= 1500 ) {
            setTextService(true);
            setTextAbout(false);
            setTextPortfolio(false);
            setTextContact(false);


        }
        else if (window.scrollY >= 1501 && window.scrollY <= 2250) {
            setTextPortfolio(true);
            setTextService(false);
            setTextAbout(false);
            setTextContact(false);

        }
        else if (window.scrollY >= 2251) {
            setTextContact(true);
            setTextPortfolio(false);

        }
        else {
            setTextAbout(false);
            setTextService(false);
            setTextPortfolio(false);
            setTextContact(false);



        }

    }

    else {
        setNavbar(false);
        setTextHome(false);
        // setTextAbout(false);
        // setTextService(false);
        // setTextPortfolio(false);
        // setTextContact(false);



    }
};
window.addEventListener('scroll', changeBackground);




return (
    <nav className={navbar ? "navbar active navbar-expand-lg navbar-light fixed-top py-3" : "navbar navbar-expand-lg navbar-light fixed-top py-3"} id="mainNav">
        <div className="container">
            <Link smooth className="navbar-brand nav-link " to="/#app">Start Bootstrap</Link>
            <button className="navbar-toggler navbar-toggler-right" type="button" data-toggle="collapse" data-target="#navbarResponsive" aria-controls="navbarResponsive" aria-expanded="false" aria-label="Toggle navigation"><span className="navbar-toggler-icon"></span></button>
            <div className="collapse navbar-collapse" id="navbarResponsive">

                    <ul className="navbar-nav ml-auto my-2 my-lg-0">
                        <li className="nav-item active"><Link smooth className="nav-link" style={{color:TextHome?"orange":"#000"}} to="/#app">Home</Link></li>
                        <li className="nav-item"><Link smooth className="nav-link  " style={{color:TextAbout?"orange":"#000"}}  to="/#about">About</Link></li>
                        <li className="nav-item"><Link smooth className="nav-link  " style={{color:TextService?"orange":"#000"}}  to="/#services">Services</Link></li>
                        <li className="nav-item"><Link smooth className="nav-link  " style={{color:TextPortfolio?"orange":"#000"}} to="/#portfolio">Portfolio</Link></li>
                        <li className="nav-item"><Link smooth className="nav-link  " style={{color:TextContact?"orange":"#000"}}  to="/#contact">Contact</Link></li>
                </ul>
            </div>
        </div>
    </nav>
);

} export default NavBar;`

Answer №7

Here is a more succinct method for changing the background color of the header.

useEffect(() => {
    let header = document.getElementById("header");
    window.addEventListener("scroll", () => {
      if (window.scrollY > 100) {
        header.style.background = "blue"
      } else {
        header.style.background = "transparent"
      }
    })
  })

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

Refreshing GIF images in React using forceReload

In order to restart the gif animation every 12 seconds or whenever the activeIndex changes, I need to reload a GIF image with CHECKMARK_ANIMATION_ICON as the source. Below is the code: const reloadImgSource = (imgSource) => { setTimeout(() =& ...

Error: Unable to execute map() function on commands.options? while configuring slash commands

discord js v13.3.1 I have configured my bot to update and deploy slash commands using a specific command called "deploy". The structure of the deploy command is as follows: module.exports = { name: "deploy", description: "deploys sl ...

JavaScript and HTML have encountered an Uncaught TypeError: The property 'addEventListener' cannot be read because it is null

Having an issue here. Whenever I try to play sound from an image, I encounter an error. Uncaught TypeError: Cannot read property 'addEventListener' of null Here is my HTML code: <html> <head> <title>Music</title> < ...

Trigger a page refresh using a popup

Exploring the world of React for the first time has been quite a journey. I've encountered a hurdle in trying to force my page to render through a popup window. The setup involves a function component with a popup window that allows text changes on t ...

"Error occurs when passing data back to main thread from a web worker: undefined data received

Hello, I’ve been experimenting with using a web worker to retrieve data and send it back to the main thread. However, I've encountered an issue with my code not working as expected. onmessage = (e) => { console.log(e); if( e.data[0] === &apos ...

Select Box in HTML now supports the addition of a Background Image, enhancing

Currently, I am trying to customize the select option box using HTML and CSS. My main goal is to incorporate a scroll feature into the option box. However, I have encountered an issue where the image of the checked box remains visible on the screen. Below ...

Is it possible to use only CSS to crop an image into a square and then shape it into

I am attempting to create a circular shape out of images that have varying sizes and shapes (some rectangular, some square, some portrait, and some landscape). When I apply either clip-path: circle(50% at 50% 50%); or border-radius: 50%;, the image transf ...

Encountering an "Unspecified Reference Error" while attempting to retrieve data from an API in your

I've been attempting to utilize a mock API from in order to fetch data for my Next.js application. However, I am consistently encountering an undefined reference error, despite following the code snippet provided in the official Next.js documentation ...

Tips for optimizing HTML and CSS for better browser performance

Just starting to learn HTML, CSS through an online course and currently working on a simple website. The table of contents on my website is centered, but when I resize the browser on my 27-inch iMac, it doesn't stay centered. Any tips? Here's a s ...

Unforeseen box model quirks found in modern browsers when styling <table> elements

Here is a sample HTML document that demonstrates the issue: <!DOCTYPE html> <html lang="en"> <head> <meta http-equiv="X-UA-Compatible" content="IE=edge" /> <meta http-equiv="Content-Type" content="text/html; ...

What is the best way to retrieve distinct objects based on LocId across all locations?

Encountering an issue while working on Angular 7: unable to return distinct or unique objects based on LocId. The goal is to retrieve unique objects from an array of objects containing all Locations. allLocations:any[]=[]; ngOnInit() { this.locationsServ ...

Resolved the time zone problem that was affecting the retrieval of data from the AWS Redshift database in Next

Currently utilizing Next.js for fetching data from AWS Redshift. When running a query from DataGrip, the results display as follows: orderMonth | repeatC | newC 2024-02-01 | 81 | 122 2024-01-01 | 3189 | 4097 However, upon retrieving the same query ...

Using callback functions in a JavaScript AJAX request

I am currently working on a function to handle an Ajax request with a callback. The main goal of this code is to send a request and display the response within a div element on my HTML page. However, I have been facing issues with the callback functionalit ...

Why do images show up on Chrome and Mozilla but not on IE?

I have tested the code below. The images display in Chrome and Mozilla, but not in IE. The image format is .jpg. Can someone please assist? bodycontent+='<tr class="span12"><td class="span12"><div class="span12"><img class="span ...

Troubleshooting: JQuery - Applying CSS to dynamically generated elements

I used JQuery to dynamically generate a table, but I'm having trouble applying CSS to the columns. You can see an example at this Codepen link here. Specifically, I need to set the width of the first column to 100px. Could someone please assist me wi ...

Adjust the dimensions of the text area to modify its height and width

Can someone help me adjust the width and height of the <textarea> tag in HTML so that it only has two lines (rows)? Right now, it's only displaying one line. Any suggestions on how to solve this issue? Even when I try changing the width to 1000 ...

Why is the JavaScript code not functioning when the page loads?

Here is the HTML code snippet: <head> <link href="/prop-view.css" media="screen" rel="stylesheet" type="text/css"> <script src="/javascripts/jquery-1.3.2.min.js" type="text/javascript"></script> <script type="tex ...

Is the Vue "Unchecking" feature not properly deleting data from the array?

I need to update my function to handle the removal of a networkAudience when its corresponding checkbox is unchecked. Currently, the networkAudience is being added to the array when checked, but not removed when unchecked. What changes should I make to en ...

The Analog Clock is unable to access the property of null

I'm struggling to understand what's wrong here. The querySelector doesn't seem to be functioning correctly as I keep encountering the error "cannot read the property of null" on line 10 of my JavaScript code. const deg = 6; const hr = docum ...

Switching the boolean property within an object located in an array of objects within the state

In my state, I have an array of objects structured like this: this.state = { week: [ { en: 'Mon', morn: false, night: false, }, //... the rest of the days... Successfully toggling the morn and night Booleans can be d ...