barbajs / barba

Create badass, fluid and smooth transitions between your website’s pages
https://barba.js.org/
MIT License
11.6k stars 477 forks source link

Urgent! Can't use vanilla event listeners w/ barba (using function to re-init scripts on newPageLoad) #350

Closed smallio closed 5 years ago

smallio commented 5 years ago

Hi,

Super close to a deadline here. I went to add event listeners & suddenly everything is going wrong. I keep getting "Cannot read property 'addEventListener' of null" on any event listeners. jQuery works fine however... although I really can't afford to add the library just for this case.

I'm assuming it's just firing before they are available to attach to, so I've tried all sorts of load listeners & events... no cigar just yet.

Any idea what I can do? Thank you in advance :)


window.addEventListener("load", function () {
   Barba.Dispatcher.on('transitionCompleted', function(currentStatus, oldStatus, container) {
        reInitializeScripts();
    });
}

function reInitializeScripts() {

  let nextButton = document.querySelector(".swipe-button-next"), 
      backButton = document.querySelector(".swipe-button-prev"), 

      nextButtonText = document.querySelector('.swiper-button-next-text'), 
      backButtonText = document.querySelector('.swiper-button-back-text'), 

      nextButtonStroke = document.querySelector('.swiper-btn-next-stroke-clr'),
      backButtonStroke = document.querySelector('.swiper-btn-back-stroke-clr');

      nextButtonArrow = document.querySelector('.fuego-button-arrow-next'),
      backButtonArrow = document.querySelector('.fuego-button-arrow-back');

  TweenMax.set([nextButtonStroke, backButtonStroke], {drawSVG: "0% 0%"});

  let nextButtonTL = new TimelineMax({paused:true, reversed:true}),
      prevButtonTL = new TimelineMax({paused:true, reversed:true});

  nextButton.addEventListener('mouseover', function() {
    nextButtonTL.to(nextButtonStroke, 0.52, {
      drawSVG:"100% 0%"
    }, 0);
    nextButtonTL.to(nextButtonText, 0.52, {
      color:"#DA8978"
    }, 0);
    nextButtonTL.to(nextButtonArrow, 0.52, {fill:"#DA8978"}, 0);
    nextButtonTL.play();
  });

  backButton.addEventListener('mouseover', function() {
    prevButtonTL.to(backButtonStroke, 0.52, {
      drawSVG:"0% 100%"
    }, 0);
    prevButtonTL.to(backButtonText, 0.52, {
      color:"#DA8978"
    }, 0);
    prevButtonTL.to(backButtonArrow, 0.52, {fill:"#DA8978"}, 0);
    prevButtonTL.play();
  });

  nextButton.addEventListener('mouseout', function() {
    nextButtonTL.reverse();
  });

  backButton.addEventListener('mouseout', function() {
    prevButtonTL.reverse();
  });
}
luruke commented 5 years ago

Hello @smallio , sorry it's not clear exactly what your problem is.

Can you expand on it a little bit?

smallio commented 5 years ago

Hey @luruke first of all love barba! It's pretty much all I use :)

So basically as mentioned (& with all ajax projects) I wrap listeners and other scripts that need to run on pages & call via "transitionCompleted" or "onNewPageReady".

Pretty much always used jQuery but this site has to be Vanilla due to loading times. When I use event listeners I can't seem to get the elements at the right stage, no matter what I do I keep getting "Cannot read property 'addEventListener' of null"... even after calling reInitScripts (which contains the listeners) wrapped in a load/DOMcontentloaded function, surely that would mean the elements are available to attach stuff to but apparently not... However using jquery to call functions like below seems to work fine:

$(".swipe-button-next").mouseenter(swipeNextHoverIn);

I'm stumped!!! Super frustrating aha.

Maybe you've seen this behaviour?

gfnool commented 5 years ago

Hi @smallio ,

when you use the jQuery selector, dom elements are wrapped in a jQuery object and this prevent any error like adding a listener to a element that does not exists in the page. That's one of the beauty of jQuery.

Your vanilla code search for elements with document.querySelector and then attach an event listener to them but without checking if the elements exist.

smallio commented 5 years ago

Hey @gfnool :)

I actually did not know that... awesome thank you :) However I'd love to know how you can make it work with vanilla. I'm gradually moving over to purely vanilla just to get better at not using libraries so I'd love to know how you'd get past this in barba. If you or @luruke could shed some light on that I'd be forever thankful as I use barba for almost every project.

:) have a great day/night!

thierrymichel commented 5 years ago

Hi @smallio,

I do not see anything wrong with your code. You can find here a small working example.

Just Barba, with a button and listeners… I think the problem is elsewhere… :/ Check the way you wait for DOM loaded?

smallio commented 5 years ago

Thank you guys, I'm going to try and bring everything to a new project & see what the problem was.

One other question... All my page transitions are gsap timelines & one page has a slider on it. When you refresh/reload the same page manually via the browser refresh icon, the slider gets destroyed. Is there anyway to trigger the play of the timelines on refresh? The home page replays timelines just not the other pages.

I've been looking at "window.onbeforeunload" but I'd love to know what you all do.

gfnool commented 5 years ago

I actually did not know that... awesome thank you :) However I'd love to know how you can make it work with vanilla. I'm gradually moving over to purely vanilla just to get better at not using libraries so

I guess that you are using the same script on every page, listening the transitionCompleted event, but not every page contains all the elements that are initialized in your script.

For example, if a page does not contain the '.swipe-button-next' element, the variable will be null and therefore the addEventListener method will thrown an error. You have to test it before like this:


var nextButton = document.querySelector(".swipe-button-next"); 

if(nextButton != null)
    nextButton.addEventListener(...);

Otherwise you can run specific code based on different namespace, have a look at the View documentation here: http://barbajs.org/views.html

In the same way you have to destroy everything when the old container is removed, to prevent memory leaks or duplicated listeners.

Do not get me wrong, but Barba has nothing to do with this problems. These are common patterns of a Single Page Application and you should understand the plugins/modules lifecycle better before using them.

gfnool commented 5 years ago

Thank you guys, I'm going to try and bring everything to a new project & see what the problem was.

One other question... All my page transitions are gsap timelines & one page has a slider on it. When you refresh/reload the same page manually via the browser refresh icon, the slider gets destroyed. Is there anyway to trigger the play of the timelines on refresh? The home page replays timelines just not the other pages.

I've been looking at "window.onbeforeunload" but I'd love to know what you all do.

I personally do not ever used window.onbeforeunload in my life. If the user click the browser's refresh button it's absolutely ok to reload the page

smallio commented 5 years ago

@gfnool Thank you for the great replies. I do use view & my code is modified for each page, but I must have missed something.

Regarding your second reply, yup I wouldn't want to stop that, I'm just wondering what the best way of re-firing the pages barba transition is on refresh? This site in the documentation does what I'm trying to do. http://www.poigneedemainvirile.com/

Cheers!

gfnool commented 5 years ago

If you want to show an animation on page refresh you do not have to use a barba transition. That's simply an animation on page start, for example entering a View or starting a Tween timeline attached to a dom element on the page.

smallio commented 5 years ago

If you want to show an animation on page refresh you do not have to use a barba transition. That's simply an animation on page start, for example entering a View or starting a Tween timeline attached to a dom element on the page.

Okay fantastic, I thought so! Cheers for all the help @gfnool :)

smallio commented 5 years ago

Ok everything is working now with no problems between page transitions (yay!!!) Thank you all for the knowledge :)

One last issue with this (I promise this is the last lol), when I refresh any page after the animation has finished I need my hover/click events to work. If I call "reInitializeScripts()" (which contains global hover events across the site) via a load/DOM content loaded function, some pages I still get "cannot split a null element". The pages I get that error on are the ones that have no global hover/click events stored in "reInitializeScripts()".

Do you guys compartmentalise your hover/clicks in separate function wraps then call them via views depending on what is needed on the page & then again for page refreshes use the same functions to call via load/DOM content loaded calls?

Sorry for the TLDR, just want to make sure I get this right for the future!!

<3

smallio commented 5 years ago

100% fixed guys! Sorry for the long thread & thank you for being patient. Fixed by separating into modules & calling via views :) (also 1 typo lol) Cheers & thank you for the fantastic work on Barba :)

Feel free to close this!

thierrymichel commented 5 years ago

Glad to hear! 👍