Dogfalo / materialize

Materialize, a CSS Framework based on Material Design
https://materializecss.com
MIT License
38.87k stars 4.74k forks source link

Make collapsible content automatically scroll to its beginning when clicked #2081

Closed chedie closed 7 years ago

chedie commented 9 years ago

Hi everyone,

I need help on this one.

I've used the collapsible/accordion component. Everything is working fine until I realized that some of my content is long, thus resulting to very long scrolling especially on mobile devices.

So what happens on mobile screens is, when I click on a collapsible content, it opens in the middle or bottom part of the content. The user will have to scroll all the way up to start reading.

So my question is, how do I make a collapsible panel scroll to the beginning or title of its content upon click to open?

I appreciate any reply. Thank you.

mig3008 commented 8 years ago

no one can help us =(

diareuse commented 7 years ago

Hey! if it's still relevant to you I've found a little workaround. Replace accordionOpen function with this one in your materialize.js.

What you care about is .slideDown code, where you animate scrollTop after animation is finished. I've added padding 64px (nav) and 6px (just for fun).

I've touched JS for the first time in my life, so it's maybe a bit unprofessional. 😃 I hope somebody will improve my answer 😄

  function accordionOpen(object) {
    $panel_headers = $this.find('> li > .collapsible-header');
    if (object.hasClass('active')) {
      object.parent().addClass('active');
    }
    else {
      object.parent().removeClass('active');
    }
    if (object.parent().hasClass('active')){
      object.siblings('.collapsible-body').stop(true,false).slideDown({ duration: 350, easing: "easeOutQuart", queue: false, complete: function() {
        $(this).css('height', '');
        var doc = document.documentElement;
        var top = (window.pageYOffset || doc.scrollTop)  - (doc.clientTop || 0);
        var objectTop = object.offset().top-64-6;
        if(top > objectTop){
          $('html, body').animate({
              scrollTop: objectTop
          }, 1000);
        }
      }});
    }
    else{
      object.siblings('.collapsible-body').stop(true,false).slideUp({ duration: 350, easing: "easeOutQuart", queue: false, complete: function() {$(this).css('height', '');}});
    }
    $panel_headers.not(object).removeClass('active').parent().removeClass('active');
    $panel_headers.not(object).parent().children('.collapsible-body').stop(true,false).each(function() {
      if ($(this).is(':visible')) {
        $(this).slideUp({
          duration: 350,
          easing: "easeOutQuart",
          queue: false,
          complete:
            function() {
              $(this).css('height', '');
              execCallbacks($(this).siblings('.collapsible-header'));
            }
        });
      }
    });
  }
graudeejs commented 7 years ago

Inspired by @diareuse answer, here's my simpler workaround:

$('.collapsible[data-collapsible="expandable"]').collapsible({
  onOpen: function(el) {
    var header = el.find('.collapsible-header');
    var doc = document.documentElement;
    var top = (window.pageYOffset || doc.scrollTop)  - (doc.clientTop || 0);
    var objectTop = header.offset().top;

    if(objectTop < top || objectTop > top + window.innerHeight - 100 ){
      $('html, body').animate({
        scrollTop: objectTop - 10
      }, 500);
    }
  }
});

Note: in my project I initialize expandables myself. In this case this code is for expandable. It won't work for accordion 100% correctly.

This way you don't need to override anything :)

tomscholz commented 7 years ago

Duplicate of: https://github.com/Dogfalo/materialize/issues/3239

PiyushMishra commented 6 years ago

@diareuse It would be good to have object.parent().css({'display':'none'}) before the animation and set the display as block so that user don't experience up/down scrolling effect.

function accordionOpen(object) {
        $panel_headers = $this.find('> li > .collapsible-header');
        $panel_headers.not(object).removeClass('active').parent().removeClass('active');
        $panel_headers.not(object).parent().children('.collapsible-body').stop(true,false).each(function() {
          if ($(this).is(':visible')) {
            $(this).slideUp({
              duration: 350,
              easing: "easeOutQuart",
              queue: false,
              complete:
                function() {
                  $(this).css('height', '');
                  execCallbacks($(this).siblings('.collapsible-header'));
                }
            });
          }
        });
        if (object.hasClass('active')) {
          object.parent().addClass('active');
        }
        else {
          object.parent().removeClass('active');
        }
        if (object.parent().hasClass('active')){
          object.parent().css({'display':'none'})
          object.siblings('.collapsible-body').stop(true,false).
          slideDown({ duration: 350, easing: "easeOutQuart", queue: false, complete: function() {
            $(this).css('height', '');
            var doc = document.documentElement;
            var top = (window.pageYOffset || doc.scrollTop)  - (doc.clientTop || 0);
            var objectTop = object.offset().top-64-6;
            if(top > objectTop){
              $('html, body').animate({
                  scrollTop: (objectTop)/3
              }, 350);
            }
          }});
          object.parent().css({'display':'block'})

        }
        else{
          object.siblings('.collapsible-body').stop(true,false).slideUp({ duration: 350, easing: "easeOutQuart", queue: false, complete: function() {$(this).css('height', '');}});
        }

      }