FrankRuiz Posted October 16, 2020 Share Posted October 16, 2020 Hello, I am experiencing this error: 'NotFoundErrror: Failed to Execute 'removeChild' on 'Node': The node to be removed is not a child of this node.' Specifically when using the 'pinning' attribute. Here is the code snippet being used in my component causing the error: useEffect(() => { panelRefs.current.forEach((panelRef, index) => { // GSAPs default 'end' value. let endValue = 'bottom top'; // Unpin the last item right away so it doesn't cover the non-hero content below it. // Still apply scrollTrigger to it so it will 'layer' over the prev image. if (index === panelRefs.current.length - 1) endValue = 'top top'; ScrollTrigger.create({ trigger: panelRef, scrub: true, start: 'top top', end: endValue, markers: true, pin: true, // BREAKS pinSpacing: false, }); }); }); Thank you in advance Link to comment Share on other sites More sharing options...
ZachSaucier Posted October 16, 2020 Share Posted October 16, 2020 Hey Frank. Can you please create a minimal demo of the issue using something like CodeSandbox? Link to comment Share on other sites More sharing options...
GreenSock Posted October 16, 2020 Share Posted October 16, 2020 Yeah, it's super difficult to troubleshoot blind. @FrankRuiz what version of ScrollTrigger are you using? Please make sure it's the latest. Also, did you gsap.registerPlugin(ScrollTrigger)? Once we see a minimal demo, I'm sure we'll be able to offer more assistance. 👍 Link to comment Share on other sites More sharing options...
FrankRuiz Posted October 16, 2020 Author Share Posted October 16, 2020 Thanks for your reply guys, here is some more context. Here's a (seemingly) working example of what we're doing. demo page: '/pages/hero-ex-page.js' component: '/components/hero.js' This code is from our actual project and just stripped down. What we're trying to understand lies in that this very implementation will break when the component is used on a specific page, but not on others. Specifically, when 'pin: true' is set. I know that's vague and difficult to help troubleshoot and that's completely understandable. If anyone has any insight into experiencing the 'NotFoundErrror: Failed to Execute 'removeChild' on 'Node': The node to be removed is not a child of this node.' when using GSAP in general, that would also help. And/or if there's anything within the implementation in the '/components/hero.js' component that raises any flags. *we know this may be a react specific question https://codesandbox.io/s/agitated-waterfall-8g3ep?file=/src/components/hero.js Link to comment Share on other sites More sharing options...
ZachSaucier Posted October 16, 2020 Share Posted October 16, 2020 I would double check what panelRef is in your project. In the code you provided in your first post here you have panelRef but in the working demo you have panelRef.current. It should probably be panelRef.current. 1 Link to comment Share on other sites More sharing options...
Rodrigo Posted October 16, 2020 Share Posted October 16, 2020 Hey Frank, You're not passing any dependencies to the useEffect hook. Is this component rendered just once in the entire lifecycle of the App? Because right now that will run everytime a state or prop in that component is updated or the parent component is re-rendered as well. Also clean up your component when it unmounts by returning a function in a useEffect hook with an empty array as dependencies: useEffect(() => { // At the end of the hook return a function return () => { // Here kill all your GSAP instances in case one of them is still running // when the component is unmounted }; }, []); Also I'm not seeing any errors in the sample you provided, the only thing that caught my attention is this: if (panelRefs.current.length !== numPanels) { panelRefs.current = Array(numPanels) .fill() .map((_, i) => { return panelRefs.current[i] || createRef() }) } This code is running every time the component is rendered, if at that point the DOM is actually updated as well, wouldn't the references to the DOM elements be updated then in the array map method? My point being, I don't see the real use for this code, even if the panelRefs are kept through re-renders, those elements in the array would be updated on the re-renders. In fact if for some reason at some point the amount of panels is less than in a previous render, you'll keep the reference in the array even though the DOM node is no longer in the DOM, thus creating a potential memory leak. Of course you know your code far better than I do, but this particular snippet stood out. Beyond that I couldn't tell what the problem could be. Happy Tweening!!! 2 Link to comment Share on other sites More sharing options...
FrankRuiz Posted October 27, 2020 Author Share Posted October 27, 2020 Thank you for your time guys, it ended up being due to a package that was being used on the component for unique ids that seemed to be mixing up the node references. This had been previously been used as the 'key' prop on the node list. 😅 1 Link to comment Share on other sites More sharing options...
eviljeff Posted April 2, 2021 Share Posted April 2, 2021 Hi @FrankRuiz, I'm having the exact same issue. When the component is unmounted I get the error Uncaught DOMException: Failed to execute 'removeChild' on 'Node': The node to be removed is not a child of this node. This is the useEffect() function useEffect(() => { const pin = gsap.to(pinnedRef.current, { duration: .3, scrollTrigger: { trigger: pinnedRef.current, start: "top top", end: "200%", pin: true, pinReparent: true, pinSpacing: "100%" } }); return () => { pin.kill(); console.log('killed component'); } }, []); I know it has been a long time ago, but are you able to give more details on how you resolved the issue? These are the packages I am importing in the component Thank you! import React, { useState, useRef, useEffect } from 'react'; import { connect, styled } from 'frontity'; import { gsap } from 'gsap'; import ArrowLink from '../UI/arrowLink'; import SwiperCore, { Pagination, Controller, EffectFade, Mousewheel } from 'swiper'; import { Swiper, SwiperSlide } from 'swiper/react'; import Animation from '../UI/animation'; import svg2 from '../../assets/img/animation-homepage-2.svg'; import tw from 'twin.macro'; Link to comment Share on other sites More sharing options...
FrankRuiz Posted April 2, 2021 Author Share Posted April 2, 2021 Hey @eviljeff My issue seems to have been different than yours. However, I think what you are getting is because you aren't cleaning up/killing the ScrollTrigger correctly in your useEffect. In my code I assign an id to the ScrollTrigger instance and then kill it like so: useEffect(() => { if (titleEl.current) { gsap.to(titleEl.current, { x: -titleEl.current.offsetWidth * 0.5, ease: 'none', scrollTrigger: { id: `${ ns }-title`, trigger: titleEl.current, start: 'top bottom', end: 'bottom top', scrub: 0.25, }, }); } return () => { // clean up ScrollTrigger instance on title element const titleElTrigger = ScrollTrigger.getById(`${ ns }-title`); if (titleElTrigger) { titleElTrigger.kill(); } }; }, []); 1 1 Link to comment Share on other sites More sharing options...
eviljeff Posted April 2, 2021 Share Posted April 2, 2021 Hi @FrankRuiz, Thank you so much for the quick reply! I have tried that out just now but I still get the same error Uncaught DOMException: Failed to execute 'removeChild' on 'Node': The node to be removed is not a child of this node. Below is my full component, do you see any other issue that could cause the error to show up? If I comment out pin: true then the error won't appear. I truly appreciate your help. Thanks import React, { useState, useRef, useEffect } from 'react'; import { connect, styled } from 'frontity'; import { gsap } from 'gsap'; import { ScrollTrigger } from "gsap/ScrollTrigger"; import ArrowLink from '../UI/arrowLink'; import SwiperCore, { Pagination, Controller, EffectFade, Mousewheel } from 'swiper'; import { Swiper, SwiperSlide } from 'swiper/react'; import Animation from '../UI/animation'; import svg2 from '../../assets/img/animation-homepage-2.svg'; import tw from 'twin.macro'; SwiperCore.use([Pagination, Controller, EffectFade, Mousewheel]); const VerticalSlider = ({ content, libraries, data }) => { const [firstSwiper, setFirstSwiper] = useState(null); const [secondSwiper, setSecondSwiper] = useState(null); const pinnedRef = useRef(null); const { slides } = content; const params1 = { direction: 'vertical', // mousewheel: { // releaseOnEdges: true, // forceToAxis: true, // eventsTarget: ".vertical-slider-wrapper" // }, speed: 1000, pagination: { clickable: true, }, slidesPerView: 1, controller: { control: secondSwiper } }; const params2 = { preloadImages: false, lazy: true, noSwiping: true, effect: 'fade', controller: { control: firstSwiper } }; useEffect(() => { if (pinnedRef.current) { let ns = Math.random().toString(36).substring(7); gsap.to(pinnedRef.current, { duration: .3, scrollTrigger: { id: `${ ns }-title`, trigger: pinnedRef.current, start: "top top", end: "200%", pin: true, pinSpacing: "100%" } }); } return () => { const titleElTrigger = ScrollTrigger.getById(`${ ns }-title`); if (titleElTrigger) { titleElTrigger.kill(); } } }, []); return ( <VerticalSliderWrapper className="vertical-slider-wrapper" ref={pinnedRef}> <VerticalSliderCol tw="z-0"> <Swiper {...params2} onSwiper={setSecondSwiper}> {slides && slides.map((slide, i) => { const { image } = slide; return ( <SwiperSlide key={i.toString()}> <ImageSlide bg={image} /> </SwiperSlide> ) })} </Swiper> </VerticalSliderCol> {data.isHome && <SvgWrapper> <Animation svg={svg2} elId="emy803gonfxd1" /> </SvgWrapper> } <VerticalSliderCol tw="z-20"> <Swiper {...params1} onSwiper={setFirstSwiper}> {slides && slides.map((slide, i) => { const { copy, heading, link_text, link_url } = slide; const Html2React = libraries.html2react.Component; const linkUrl = libraries.source.normalize(link_url); return ( <SwiperSlide key={i.toString()}> <ContentSlide> <div> <p>{heading}</p> <Html2React html={copy} /> {linkUrl && <ArrowLink link={linkUrl} text={link_text} className="arrow-green" />} </div> </ContentSlide> </SwiperSlide> ) })} </Swiper> </VerticalSliderCol> </VerticalSliderWrapper> ); }; export default connect(VerticalSlider); const VerticalSliderWrapper = tw.div` relative flex flex-wrap md:flex-nowrap pt-10 md:pt-20 max-w-full max-h-screen `; const VerticalSliderCol = tw.div` md:w-1/2 `; const ImageSlide = styled.div` ${tw`bg-cover h-full w-full`} background-image: url(${props => props.bg}) `; const ContentSlide = styled.div` ${tw`p-10 md:p-14 md:pr-20 2xl:p-20 2xl:pr-40 flex items-center h-full`} `; const SvgWrapper = styled.div` ${tw`absolute w-3/5 z-10 pointer-events-none`} right: 25vw; top: -25vh; `; Link to comment Share on other sites More sharing options...
FrankRuiz Posted April 2, 2021 Author Share Posted April 2, 2021 @eviljeff In your useEffect try just adding a check const pinnedRefEl = pinnedRef.current; if (!pinnedRefEl) return; // rest of your animation code Link to comment Share on other sites More sharing options...
eviljeff Posted April 2, 2021 Share Posted April 2, 2021 Thank you. I've tried that but unfortunately it doesn't fix it. The animation doesn't fire when navigating away but I still get the same error. I believe that the .kill() function doesn't really work..? If I console log titleElTrigger before and after the kill it looks like the ScrollTrigger is still alive. Is that correct? return () => { const titleElTrigger = ScrollTrigger.getById(`${ ns }-title`); if (titleElTrigger) { console.log(titleElTrigger); titleElTrigger.kill(); console.log('After the kill ======='); console.log(titleElTrigger); } } // the console logs look identical ScrollTrigger {start: 5034, progress: 0, media: undefined, scroller: Window, scroll: ƒ, …} After the kill ======= ScrollTrigger {start: 5034, progress: 0, media: undefined, scroller: Window, scroll: ƒ, …} Thank you so much for the help. Link to comment Share on other sites More sharing options...
FrankRuiz Posted April 2, 2021 Author Share Posted April 2, 2021 @eviljeff hmm not sure, it should be removing that ScrollTrigger instance, maybe try just hard coding a string ID and not using a random one. Logging this will show you all the accumulated ST instance - ScrollTrigger.getAll( ) Link to comment Share on other sites More sharing options...
eviljeff Posted April 2, 2021 Share Posted April 2, 2021 I did try hard coding a string ID and that didn't make a difference. If I run ScrollTrigger.getAll() on the useEffect unmount I get all of my 4 ScrollTrigger instances. Should I see 3 in the array if the kill function I'm trying to run would execute correctly? The one I'm trying to kill is the second one in the array. Link to comment Share on other sites More sharing options...
eviljeff Posted April 16, 2021 Share Posted April 16, 2021 Hi @FrankRuiz I've still not resolved this issue unfortunately. I've read that your issue was being due to a package. Could you please let me know what package that was? I'm using Frontity, which is a framework a bit like Gatsby so perhaps they use similar packages. Thanks you Link to comment Share on other sites More sharing options...
djdnl13 Posted May 13, 2021 Share Posted May 13, 2021 Hi @FrankRuiz, Have you tried surrounding your pinnedRef with a <div> ... </div> ? <div> <VerticalSliderWrapper className="vertical-slider-wrapper" ref={pinnedRef}> ... </VerticalSliderWrapper> </div> I have a similar issue when changing routes on react router, and this simple change fixed my problem. Cheers 1 Link to comment Share on other sites More sharing options...
OSUblake Posted June 3, 2021 Share Posted June 3, 2021 For anyone else coming across this problem, the solution is to use useLayoutEffect as shown in this post. 2 1 Link to comment Share on other sites More sharing options...
Recommended Posts
Create an account or sign in to comment
You need to be a member in order to leave a comment
Create an account
Sign up for a new account in our community. It's easy!
Register a new accountSign in
Already have an account? Sign in here.
Sign In Now