0

Summary: I have this logo in my website header, I want this logo (image [1]) to change to another one (image [2]) on scrolling down, and to change back to the original logo (image [1]) when I scroll back to the top.

What i tried: I tried to make it with EventListener and useEffect in the header page, but I'm getting this error below:

    ERROR in src\layouts\Navbar\index.jsx
    Line 12:3:  'listenScrollEvent' is not defined  no-undef

My code:

import React, { useState } from 'react'
import { useEffect } from "react";


export default () => {

  useState = {
    imageSrc: '',
    imageAlt: ''
  }

  listenScrollEvent = e => {
    if (window.scrollY > 400) {
      this.setState({
        imageSrc: './/img/Smartlogger_logo.png',
        imageAlt: 'smartlogger white logo'
      })
    } else {
      this.setState({
        imageSrc: './../../views/Home/image/smartLoggerheader_logo.png',
        imageAlt: 'smartlogger colored logo'
      })
    }
  }

  
  useEffect(() => {
    window.addEventListener('scroll', this.listenScrollEvent)
  }, []);


  return (
    <header className='header-area header-sticky'>
      <div className='container'>
        <div className='row'>
          <div className='col-12'>
            <nav className='main-nav'>
              {/* ***** Logo Start ***** */}
              <a href='/#' className='logo'>
                <style>{css}</style>
                <img
                  src={this.setState}
                  alt='Smartlogger logo'
                />
              </a>
              {/* ***** Logo End ***** */}
              </nav>
              </div>
            </div>
          </div>
        </header>
      )
    }
Ingo Steinke
  • 766
  • 3
  • 11
  • 30
dark
  • 91
  • 1
  • 17
  • Seems like you have udpated your question. previously you were using ```componentDidMount()```. I have answerd as per the initial details. you can modify it to functional component if you want to. – Ankit Saxena Mar 11 '22 at 08:36
  • 2
    that's true, thanks for the quick help. imma test it now and reply with results in few minutes – dark Mar 11 '22 at 08:39

2 Answers2

1

You need to make the following changes in your code. It should fix the issue.

  • Inside render() - img src replace src={this.setState} with src={this.state.imageSrc}
  • Inside listenScrollEvent function replace window.scrollY with event.srcElement.body.scrollY

it will look like this (I have used random images here):

  listenScrollEvent = event => {
    if (event.srcElement.body.scrollY > 400) {
      this.setState({
        imageSrc: 'https://c.tenor.com/57w9du3NrV0AAAAS/css-html.gif',
        imageAlt: 'smartlogger white logo'
      })
    } else {
      this.setState({
        imageSrc: 'https://c.tenor.com/57w9du3NrV0AAAAS/css-html.gif',
        imageAlt: 'smartlogger colored logo'
      })
    }
  }

Full working code : (I have added style={{height:'200vh'}} on container div just to test it on my local. You can remove it)

import React from 'react'
import { Link } from 'react-router-dom'

export default class App extends React.Component {

state = {
    imageSrc: 'https://c.tenor.com/TReUojNlZ6wAAAAi/js-javascript.gif',
    imageAlt: ''
  }

  listenScrollEvent = event => {
    if (event.srcElement.body.scrollY > 400) {
      this.setState({
        imageSrc: 'https://c.tenor.com/57w9du3NrV0AAAAS/css-html.gif',
        imageAlt: 'smartlogger white logo'
      })
    } else {
      this.setState({
        imageSrc: 'https://c.tenor.com/57w9du3NrV0AAAAS/css-html.gif',
        imageAlt: 'smartlogger colored logo'
      })
    }
  }

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

  render() {
  return (
    <header className='header-area header-sticky'>
      <div className='container' style={{height:"200vh"}}>
        <div className='row'>
          <div className='col-12'>
            <nav className='main-nav'>
              {/* ***** Logo Start ***** */}
              <a href='/#' className='logo'>
                {/* <style>{css}</style> */}
                <img
                  src={this.state.imageSrc}
                  alt='Smartlogger logo'
                />
              </a>
              {/* ***** Logo End ***** */}
          </nav>
          </div>
        </div>
      </div>
    </header>
  )
  }
}

Hope that's how you wanted it to work. Try running it on your local and then you can modify it as per your requiremnets.

Ankit Saxena
  • 1,067
  • 1
  • 4
  • 16
  • i tried your code but the error `'listenScrollEvent' is not defiefined` still persist – dark Mar 11 '22 at 08:47
  • 2
    Here the full view of my code https://ibb.co/WBKP0hN – dark Mar 11 '22 at 08:49
  • looking into it. till then you can check mine working code here - https://jsfiddle.net/Lrsc3nyj/. – Ankit Saxena Mar 11 '22 at 08:52
  • 1
    I approve, that actually works like i expected, hmm i dont understand why listenscrollevent still not defined in my local though... – dark Mar 11 '22 at 08:56
  • @dark can you try removing ```this``` keyword from ```this.listenScrollEvent```? as you are using a functional component now. And update listenScrollEvent to an arrow function like ```const listenScrollEvent = (event) => { // code//} ``` – Ankit Saxena Mar 11 '22 at 08:57
  • the error has gone (no more errors) but my localhost in the browser shows a totally white page. – dark Mar 11 '22 at 09:05
  • i hope this new screenshot helps https://ibb.co/q7N9zwY – dark Mar 11 '22 at 09:06
  • the issue is in the usage of usetate hook (usestate always return an array of 2 elemtents but in your case it's only one) @coglialoro has mentioned it in his answer. please have a look – Ankit Saxena Mar 11 '22 at 09:14
0

In your useState hook you want to have your src and your alt. Remember useState return an array of 2 elements, the first is the value and the second is the setter for that value. You can use the setter in your listenScrollEvent function and you can use the value in your jsx.

You are also using a css variable in you jsx that isn't defined anywhere.

It should look something like this:

import React, { useState, useEffect } from "react";

export default () => {
    const [image, setImage] = useState({
        src: "",
        alt: "",
    });

    const listenScrollEvent = (e) => {
        if (window.scrollY > 400) {
            setImage({
                src: "../img/Smartlogger_white_logo.png",
                alt: "smartlogger white logo",
            });
        } else {
            setImage({
                src: "../img/Smartlogger_colored_logo.png",
                alt: "smartlogger colored logo",
            });
        }
    };

    useEffect(() => {
        window.addEventListener("scroll", listenScrollEvent);
    }, []);

    return (
        <header className="header-area header-sticky">
            <div className="container">
                <div className="row">
                    <div className="col-12">
                        <nav className="main-nav">
                            {/* ***** Logo Start ***** */}
                            <a href="/#" className="logo">
                                <style>{css}</style>
                                <img src={require(image.src)} alt={image.alt} />
                            </a>
                            {/* ***** Logo End ***** */}
                        </nav>
                    </div>
                </div>
            </div>
        </header>
    );
};

An alternative solution that looks a little cleaner to me:

import React, { useState, useEffect } from "react";

import whiteLogo from "../img/Smartlogger_white_logo.png";
import coloredLogo from "../img/Smartlogger_colored_logo.png";

export default () => {
    const [isScrolled, setIsScrolled] = useState(false);

    const listenScrollEvent = (e) => setIsScrolled(window.scrollY > 400);

    useEffect(() => {
        window.addEventListener("scroll", listenScrollEvent);
    }, []);

    return (
        <header className="header-area header-sticky">
            <div className="container">
                <div className="row">
                    <div className="col-12">
                        <nav className="main-nav">
                            {/* ***** Logo Start ***** */}
                            <a href="/#" className="logo">
                                <style>{css}</style>
                                <img
                                    src={isScrolled ? whiteLogo : coloredLogo}
                                    alt={
                                        isScrolled
                                            ? "SmartLogger white logo"
                                            : "SmartLogger colored logo"
                                    }
                                />
                            </a>
                            {/* ***** Logo End ***** */}
                        </nav>
                    </div>
                </div>
            </div>
        </header>
    );
};

EDIT: added note about css variable, fixed typo in code and formatted better

EDIT2: fixed image link and added the require in the img src attribute, this should fix the image loading

EDIT3: added alternative solution

coglialoro
  • 601
  • 1
  • 4
  • 12
  • thanks for the help, i'm testing your code, and i will provide with the feedback asap – dark Mar 11 '22 at 09:19
  • 1
    Thanks again and i appreciate your help. I recorded the results here : https://vimeo.com/687046894 . As you can see, it works but the image doesn't want to load even though i checked the src path and it is correct here a screenshot showing my files directory : https://ibb.co/5Mg8wJP – dark Mar 11 '22 at 09:50
  • first: that path doesn't look correct to me. it should be "../img/Smartlogger_logo.png", it's 2 dots, 1 slash, not 1 dot 2 slashes :) second: try reading this https://stackoverflow.com/questions/39999367/how-do-i-reference-a-local-image-in-react – coglialoro Mar 11 '22 at 09:58
  • Edited my previus solution to fix the image loading issue and added an alternative solution that looks cleaner to me – coglialoro Mar 11 '22 at 10:13