Launching css animation on Dash page when an element, like Div, graph, is visible in viewport

I would like to launch the css animation when an element, like Div, graph, is visible in viewport. How can I catch this event to change its styée and launch the css animation? Thanks.

It looks like the issue will be resolved with the introduction of @scroll-timeline. Does anyone have idea when it will be added to the browsers?

Hi @jigu

You can place any css file in the assets folder, this folder must be in the same folder where your app is running.

Dash will take all the static files from that folder at the begining of the process (css, js, image, and also the favicon)

Hi @Eduardo ,

Thank you for your message, but I know this. The issue is that I want to apply css animation only when the element is in the viewport. I want the graph to take the whole screen by applying the animation style to it. I need this to give the modern feeling to Dash Plotly by adding fly-in/fly-out divs.

It would be very easy to resolve this issue if I could use JavaScript to trigger the element-in-viewport event. But how to do this with Dash if I cannot use native JS with the Dash Plotly React?

See if this link could help:
https://dash.plotly.com/clientside-callbacks

@Eduardo
Perhaps, some concrete suggestion would be more helpful instead of posting common knowledge info.

Feedback for @admins . The forum should have -1 or dislike button to prevent posting common knowledge comments. Otherwise, I see some people just try to get popularity. Sorry for harsh words.

Hi @jigu

Thanks for your words. I’m a 58 year old man trying to get some popularity in a community forum. :joy: :joy:

For your information, not all the community visitors know where to find documented information about an specific issue, specilly those who joined the community for the first time.

You said that wanted to use javascript to solve your issue and I just give you a link where you can do whatever you want in js.

Best. :grinning:

@Eduardo

Sorry. I am not young either, 47. My bad. I mentioned before though that according to the documentation JQuery does not work well with React. So, setting an event to catch the element in the viewport is not an option. Using the native JavaScript is difficult, because AFAIK you cannot catch an event.

It would be good to hear ideas from someone who has experience with adding external JavaScript listeners to the existing Dash Plotly React model.

Could you post the javascript code that you would “normally” use?

@Emil

Here is an example of JS to check if an element is in the viewport. I would register the listener in JQuery for scroll event

function isInViewport(element) {
    const rect = element.getBoundingClientRect();
    return (
        rect.top >= 0 &&
        rect.left >= 0 &&
        rect.bottom <= (window.innerHeight || document.documentElement.clientHeight) &&
        rect.right <= (window.innerWidth || document.documentElement.clientWidth)
    );
}

scroll event

window.addEventListener('scroll', function (event) {
	if (isInViewport(image)) {
		image.innerHTML = '<img src="' + image.getAttribute('data-image') + '">';
	}
}, false);

And what is the image variable?

At the top of my head, one option could be to create a small React component that listens to scroll events and updates a prop. I did that previously for keyboard events. You could then create a callback with that prop as input.

@Emil

It is more complex than that. I took JS on one of the troubleshooting websites. See video that shows what I have now. Right now I have to use the red button at the bottom of the screen to trigger animation.

I want to replace the red button by automatic calling of animation when you scroll down. I do not see how I can do this with JavaScript.

video

. Writing a separate component is not an option as it happens with many components as you can see on the video. I absolutely need to capture scroll down event. Perhaps, the JS code will work without JQuery.

@Emil

As suspected, the code below inserted as a separate JS file, did not give expected result for the element with ID = “visitors-container”.

The alert message is not triggered at scrolling. Any ideas?

function isInViewport(element) {
    const rect = element.getBoundingClientRect();
    return (
        rect.top >= 0 &&
        rect.left >= 0 &&
        rect.bottom <= (window.innerHeight || document.documentElement.clientHeight) &&
        rect.right <= (window.innerWidth || document.documentElement.clientWidth)
    );
}

window.addEventListener('scroll', function (event) {
	var visitor_div = document.getElementById("visitors-container");
	
	if (isInViewport(visitor_div)) {
		alert('Visible');
	}
}, false);

It worked. The JS script above was for the whole element on the screen. It did not trigger when only part was visible. Now I will work to trigger animation without the red button.

Resumé: it is enough to insert JS code as below to trigger scroll event when part of the element is visible.

Thanks @Eduardo and @Emil . This discussion helped me to find the solution. This solution will help you turn Dash into data stories with animation.

function isInViewport(element) {
    const rect = element.getBoundingClientRect();
    return (
        rect.top >= 0
    );
}

window.addEventListener('scroll', function (event) {
	var visitor_div = document.getElementById("visitors-container");
	
	if (isInViewport(visitor_div)) {
		alert('Visible');
	}
}, false);
1 Like

FYI, listening to scroll events is not efficient and may create some laggy effect in the browser.
You can instead use an Intersection Observer to capture when an element is in the viewport (and what % of it is in the viewport).

The React Intersection Observer could also probably be ported to Dash directly to avoid using custom javascript.

Thank you @RenaudLN . I will look into this.

Could you help me please use the React Intersection Observer. I’m trying to use the intersection observer to detect if the section is taking more than 50% of the page and if so, I will change the style of this element. Here is the JavaScript code and I works fine using vanilla HTML CSS JS but when using it as custom script in dash, it doesn’t work at all:

function activateNavigation() {
  const sections = document.querySelectorAll(".section");
  const navLinks = document.querySelectorAll(".nav-link");

  const observer = new IntersectionObserver(
      (entries) => {
          navLinks.forEach((navLink) => {
              navLink.classList.remove("nav-link-selected");
          });

          const visibleSection = entries.find((entry) => entry.isIntersecting);

          if (visibleSection) {
              const visibleSectionId = visibleSection.target.getAttribute("id");
              const correspondingNavLink = document.querySelector(`.nav-link[href="#${visibleSectionId}"]`);
              if (correspondingNavLink) {
                  correspondingNavLink.classList.add("nav-link-selected");
              }
          }
      },
      { threshold: 0.5 }
  );

  sections.forEach((section) => observer.observe(section));
}

document.addEventListener("DOMContentLoaded", activateNavigation);

For anyone finding this thread, I have created a dash_intersection_observer component to bring this straight into Dash and allow to use callbacks / clientside callbacks with it.

Full post here.

2 Likes