Dawn - smooth accordion open & close

Solved

Dawn - smooth accordion open & close

INFRA
Shopify Partner
243 2 85

Hi there,

 

I used below code to get a smooth opening of the accordions however this somehow prevented me from clicking on any navigation buttons/search button.

Is there a better code to achieve this than below?

 

THanks!

 

class Accordion {
  constructor(el) {
    this.el = el;
    this.summary = el.querySelector('summary');
    this.content = el.querySelector('.accordion__content') || el.querySelector('.content');
    this.animation = null;
    this.isClosing = false;
    this.isExpanding = false;
    this.summary.addEventListener('click', (e) => this.onClick(e));
  }

  onClick(e) {
    e.preventDefault();
    this.el.style.overflow = 'hidden';

    if (this.isClosing || !this.el.open) {
      this.open();
    } else if (this.isExpanding || this.el.open) {
      this.shrink();
    }
  }

  shrink() {
    this.isClosing = true;

    const startHeight = `${this.el.offsetHeight}px`;
    const endHeight = `${this.summary.offsetHeight}px`;

    if (this.animation) {
      this.animation.cancel();
    }

    this.animation = this.el.animate({
      height: [startHeight, endHeight]
    }, {
      duration: 500,
      easing: 'ease-in'
    });

    this.animation.onfinish = () => this.onAnimationFinish(false);
    this.animation.oncancel = () => this.isClosing = false;
  }

  open() {
    this.el.style.height = `${this.el.offsetHeight}px`;
    this.el.open = true;

    window.requestAnimationFrame(() => this.expand());
  }

  expand() {
    this.isExpanding = true;

    const startHeight = `${this.el.offsetHeight}px`;
    const endHeight = `${this.summary.offsetHeight + this.content.offsetHeight}px`;

    if (this.animation) {
      this.animation.cancel();
    }

    this.animation = this.el.animate({
      height: [startHeight, endHeight]
    }, {
      duration: 400,
      easing: 'ease-out'
    });

    this.animation.onfinish = () => this.onAnimationFinish(true);
    this.animation.oncancel = () => this.isExpanding = false;
  }

  onAnimationFinish(open) {
    this.el.open = open;
    this.animation = null;
    this.isClosing = false;
    this.isExpanding = false;
    this.el.style.height = '';
    this.el.style.overflow = '';
  }
}

document.querySelectorAll('details').forEach((el) => {
  new Accordion(el);
});

 

website 

Accepted Solution (1)

tim
Shopify Partner
4507 536 1643

This is an accepted solution.

This is because navigation and search popup use details elements too.

 

You should try limiting your code to accordions inside main template area, by changing the bottom part of your code like this:

document.querySelectorAll('main details').forEach((el) => {
  new Accordion(el);
});

This would only target details inside template and not in header or overlays. Like product page accordions or faqs...

 

If my post is helpful, hit the thumb up button -- it will help others with similar problem to find a solution.
I can be reached via e-mail tairli@yahoo.com

View solution in original post

Replies 4 (4)

tim
Shopify Partner
4507 536 1643

This is an accepted solution.

This is because navigation and search popup use details elements too.

 

You should try limiting your code to accordions inside main template area, by changing the bottom part of your code like this:

document.querySelectorAll('main details').forEach((el) => {
  new Accordion(el);
});

This would only target details inside template and not in header or overlays. Like product page accordions or faqs...

 

If my post is helpful, hit the thumb up button -- it will help others with similar problem to find a solution.
I can be reached via e-mail tairli@yahoo.com
INFRA
Shopify Partner
243 2 85

thanks for explaining, that worked perfectly!

INFRA
Shopify Partner
243 2 85

@tim I've added a code now so only one accordion panel remains open at a time, but it's kinda glitchy (the text of both tabs overlaps for a second when they open/close). Would there be a better solution for this?

tim
Shopify Partner
4507 536 1643

The reason is because you're animating height of details but it does not always have overflow: hidden set -- you set it in OnClick, but not in shrink, which is what you call directly when another accordion is being opened.

 

Moving  this.el.style.overflow = "hidden" into  shrink and open should solve the problem.

 

Now, the theme does some JS processing on these as well, so there may be interference also (say, aria stuff may be messed up when you directly manipulate open attribute).

 

I, myself would rather use CSS only approach, something like https://developer.mozilla.org/en-US/docs/Web/CSS/::details-content#transition_example 

If my post is helpful, hit the thumb up button -- it will help others with similar problem to find a solution.
I can be reached via e-mail tairli@yahoo.com