Search code examples
jqueryhtmlcsszurb-foundationgridstack

How to operate on element using jQuery whilst it is display:none


Scenario

I have a dashboard with some widgets grouped into tabs that the user can click through. The widgets have a default start height, and when the content (charts, graphs, tables etc.) they contain renders their heights get updated.

I currently update the heights of the widgets in only the active tab on page load and then when the user clicks a different tab update those widgets.

Problem

I'm having to use a setTimeout to make sure the active tab is visible before updating the heights, which means the widgets slide down momentarily after a click rather than appearing at their optimum height immediately.

Questions

  1. Is there a way to update the heights of all widgets on load, even the ones hidden on other tabs with display:none?

I tried using :visible and :hidden selectors but it didn't work.

  1. Should I stick with my current method for performance, since there could eventually be many widgets across many tabs? If so, can it be done without a setTimeout since I'm only guessing that 200ms will be enough of a delay.

Stripped example

Example below, plus Codepen.

$(document).foundation();

$(function () {
  var options = {
    cellHeight: 40,
    verticalMargin: 28,
    animate: true,
  };

  widgetsInit = function(options) {
    var targetPanel = '.tabs-panel.is-active';

    setTimeout(function(){
      $(targetPanel + ' .grid-stack').gridstack(options);

      $(targetPanel + ' .grid-stack .grid-stack-item').each(function(i){
        $(targetPanel + ' .grid-stack').data('gridstack').resize(
          $(targetPanel + ' .grid-stack .grid-stack-item')[i],
          $($(targetPanel + ' .grid-stack .grid-stack-item')[i]).attr('data-gs-width'),
          Math.ceil(($(targetPanel + ' .grid-stack .grid-stack-item .grid-stack-item-content')[i].scrollHeight + $(targetPanel + ' .grid-stack').data('gridstack').opts.verticalMargin) / ($(targetPanel + ' .grid-stack').data('gridstack').cellHeight() + $(targetPanel + ' .grid-stack').data('gridstack').opts.verticalMargin))
        );
      });
    }, 200);
  }

  widgetsInit(options);

  $('.tabs-title').on('click', function(e) {
    widgetsInit(options);
  })
});
.grid-stack > .grid-stack-item > .grid-stack-item-content {
    background-color: #fff;
    cursor: move;
    border: 1px solid #e3e3e3;
    border-radius: 4px;
	  padding: 1rem;
}
<link href="https://cdnjs.cloudflare.com/ajax/libs/gridstack.js/0.4.0/gridstack.min.css" rel="stylesheet"/>
<link href="https://cdnjs.cloudflare.com/ajax/libs/foundation/6.4.3/css/foundation.min.css" rel="stylesheet"/>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jqueryui/1.12.1/jquery-ui.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.10/lodash.min.js"></script>
<script src='https://cdnjs.cloudflare.com/ajax/libs/gridstack.js/0.4.0/gridstack.min.js'></script>
<script src='https://cdnjs.cloudflare.com/ajax/libs/gridstack.js/0.4.0/gridstack.jQueryUI.min.js'></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/foundation/6.4.3/js/foundation.min.js"></script>

<!-- Tabs start -->
<ul class="tabs dashboard-tabs" data-tabs id="example-tabs" data-deep-link="true" data-update-history="true" data-match-height="false">
  <li class="tabs-title is-active"><a href="#panel1" aria-selected="true">Panel 1</a></li>
  <li class="tabs-title"><a href="#panel2">Panel 2</a></li>
</ul>
<!-- Tabs end -->

<!-- Panels start -->
<div class="tabs-content unstyled" data-tabs-content="example-tabs">
  
  <!--  Panel 1 start  -->
  <div class="tabs-panel is-active" id="panel1">
    <div class="grid-stack">
      <div class="grid-stack-item" data-custom-id="0" data-gs-x="0" data-gs-y="0" data-gs-width="4">
        <div class="grid-stack-item-content">
          #0<br>
          <iframe width="560" height="315" src="https://www.youtube.com/embed/VUuBJ0ietME" frameborder="0"></iframe>
          </div>
      </div>
      <div class="grid-stack-item" data-custom-id="1" data-gs-x="4" data-gs-y="0" data-gs-width="4">
        <div class="grid-stack-item-content">
          #1
        </div>
      </div>
      <div class="grid-stack-item" data-custom-id="2" data-gs-x="8" data-gs-y="0" data-gs-width="4">
        <div class="grid-stack-item-content">#2</div>
      </div>
      <div class="grid-stack-item" data-custom-id="3" data-gs-x="0" data-gs-y="1" data-gs-width="8">
        <div class="grid-stack-item-content">#3</div>
      </div>
    </div>
  </div>
  <!-- Panel 1 end -->
  
  <!-- Panel 2 start -->
  <div class="tabs-panel" id="panel2">
    <div class="grid-stack">
      <div class="grid-stack-item" data-custom-id="4" data-gs-x="0" data-gs-y="0" data-gs-width="6">
        <div class="grid-stack-item-content">
          #4<br>
          <iframe width="560" height="315" src="https://www.youtube.com/embed/a4fv-BtzNmY" frameborder="0"></iframe>
        </div>
      </div>
      <div class="grid-stack-item" data-custom-id="5" data-gs-x="6" data-gs-y="0" data-gs-width="6">
        <div class="grid-stack-item-content">#5</div>
      </div>
    </div>
  </div>
  <!-- Panel 2 end -->
  
</div>
<!-- Panels end -->


Solution

  • There are a few things at play here. I've created a fork on how I would handle it. https://codepen.io/matthewblewitt/pen/KeybxG

    I've triggered the gridstack plugin with the foundation tabs API rather than a click event so you can remove the setTimeout.

    $(document).on('change.zf.tabs', function(e) {
       widgetsInit(options);
    });
    

    The foundation tabs hide inactive tabs with display: none and the gridstack plugin doesn't seem to play nice with that property. So I've changed the CSS to hide inactive tabs off screen instead and fade them with CSS opacity and disabled gridstack's animations.

    .tabs-panel {
      position: absolute; 
      overflow: hidden; 
      height: 1px; 
      width: 1px; 
      opacity: 0;
      transition: 0.75s opacity ease;
      display: block;
    }
    .tabs-panel.is-active {
      position: static;
      overflow: visible;
      width: auto;
      height: auto; 
      opacity: 1;
    }
    

    Hope this helps.