Home > Enterprise >  combine tabs and grid layout
combine tabs and grid layout

Time:03-22

I'm currently learning, please bear with me. I try to make a holy grail layout using grid while making tabs to change middle content (and maybe left one in the future).

I used this suggestion I found here to make the tabs : https://jsfiddle.net/a2fh8n4e/

$(document).ready(function () {
    var previousActiveTabIndex = 0;

    $(".tab-switcher").on('click', function (event) {
        if (event.type === "click") {
            var tabClicked = $(this).data("tab-index");
            if(tabClicked != previousActiveTabIndex) {
                $("#allTabsContainer .tab-container").each(function () {
                    if($(this).data("tab-index") == tabClicked) {
                        $(".tab-container").hide();
                        $(this).show();
                        previousActiveTabIndex = $(this).data("tab-index");
                        return;
                    }
                });
            }
        }
    });
});
html {
  background-color: #aaaaaa;
  font-size: 16px;
  font-family: "alegreya", serif;
}

body {
  min-width: 550px;
  margin: 0;
  padding: 0 20px 20px 20px;
  display: grid;
  grid-template-columns: 15% auto 15%;
  grid-template-areas: "header header header" "left middle right" "footer footer footer";
}

#header {
  border-width: 2px, 2px, 0, 2px;
  border-color: #ccc;
  border-style: solid;
  margin-top: 10px;
}

#left {
  border-width: 4px, 1px, 0px, 2px;
  border-color: #ccc;
  border-style: solid;
}

#middle {
  border-width: 0, 1px, 2px, 1px;
  border-color: #ccc;
  border-style: solid;
}

#right {
  border-width: 0, 2px, 2px, 1px;
  border-color: #ccc;
  border-style: solid;
  padding-left: 3px;
}

#footer {
  background: #ccc;
  height: 40px;
  padding-top: 10px;
  position: absolute;
  bottom: 0;
  left: 0;
  right: 0;
  text-align: right;
}
  
.tab-switcher {
    display: inline-block;
    cursor: pointer;
    margin-right: 25px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>

<!-- HEADER AND TABS -->
<div style="grid-area: header" id="header">
  <ul>
    <li  data-tab-index="0" tabindex="0">Collecte</li>
    <li  data-tab-index="1" tabindex="0">Journal</li>
  </ul>
</div>

<!-- CONTENT ZONE -->
<div id="allTabsContainer">
  <div  data-tab-index="0">
    <!-- TAB1 -->
    <div style="grid-area: left" id="left">
      <p>Wood : <span id="Wood"></span></p>
    </div>
    <div style="grid-area: middle" id="middle">
      <button  id="But_wood">Pick wood</button>
      <button  id="But_tree">Cut a tree</button>
    </div>
  </div>

  <div  data-tab-index="1" style="display:none;">
    <!-- TAB2 -->
    <div style="grid-area: left" id="left">
      <p>Stone : <span id="Stone"></span></p>
      <p>Grass : <span id="Grass"></span></p>
    </div>
    <div style="grid-area: middle" id="middle">
      <button  id="But_stone">Collect stone</button>
      <button  id="But_grass">Collect grass</button>
    </div>
  </div>
  <!--div  data-tab-index="2" style="display:none;">
        Some content for Tab - 3
      </div-->
</div>

<!-- LOGS & CHAT SHOULD BE ALWAYS ACTIVE -->
<div style="grid-area: right" id="right">
  <p>Logs</p>
  <p id="log">...</p>
</div>

<!-- FOOTER SHOULD BE ALWAYS ACTIVE -->
<div style="grid-area: footer" id="footer">
  <p>contenu relatif au site et à l'auteur</p>
</div>

<!-- MISC. & SCRIPTS -->
<script src="scripts/main.js"></script>

  • I tried to combine the two but can't reach something where it works as intended with the correct layout. I can not combine style="grid-area: xxx within the selected tabs https://jsfiddle.net/j6cemskg/35/ => I want the buttons to be in the middle column, while having ressources amount on a left panel, and both should change according to tab chosen.

  • Also, if anyone can give insight on how to avoid the trouble to have multiple ID like "left0","left1", etc. for each and every tab (maybe class attribute ? Can I have multiple classes assigned to a same element ?)

I would gladly take any advice concerning those troubles, thank you.

CodePudding user response:

One approach is as follows, but I've changed quite a bit of your code to simplify things.

Basically, I've adjusted your HTML so that the grid display is based on the #allTabsContainer element, and made all elements that make up areas of that grid siblings in order that they can be placed in the grid defined on the #allTabsContainer element.

I've also removed the jQuery entirely, and revised the approach used to toggle the display of the various grid-elements.

Explanatory comments for changes, and functionality, are in the code below:

// defining a named function using Arrow syntax, to which a reference to the
// Event Object is passed (automatically) from EventTarget.addEventListener():
const   updateToggle = (evt) => {
        // we retrieve the element that has triggered the function-call to
        // which the function was bound:
        let switched = evt.currentTarget,
            // from that element we use the HTMLElement.dataset API to
            // retrieve the value of the named attribute; we use camelCase
            // to access attributes that include hyphens; so 'data-tab-index'
            // is accessed as below:
                toShow = switched.dataset.tabIndex,
            // from the switched element we navigate to the closest ancestor
            // that contains a 'data-tab-show' attribute:
            toggleDispatch = switched.closest('[data-tab-show]');
            
        // with that Element we - again - utilise the HTMLElement.dataset
        // API to set the the 'data-to-show' attribute-value to the value
        // we retrieved from the clicked ('switched') element:
        toggleDispatch.dataset.tabShow = toShow;
      },
      // here we retrieve the elements that will serve as switches, the
      // li.tab-switcher elements:
            toggles = document.querySelectorAll('li.tab-switcher');
      
// we use NodeList.prototype.forEach() to iterate over the NodeList:
toggles.forEach(
  // here we use an Arrow function to bind the updateToggle() function
  // (note the deliberately omitted parentheses) as the event-handler
  // for the 'click' event on the elements of the NodeList:
    (el) => el.addEventListener('click', updateToggle)
);
/* simple reset to ensure that all elements are using the same
   box-sizing algorithm, font, list-style-type, margin and padding */
*, ::before, ::after {
  box-sizing: border-box;
  font: normal normal 1rem / 1.5 sans-serif;
  list-style-type: none;
  margin: 0;
  padding: 0;
}

#allTabsContainer {
  display: grid;
  /* setting a 0.5em gap between adjacent elements,
     whether vertically, or horizontally, adjacent: */
  gap: 0.5em;
  grid-template-areas:
    "header header header"
    "left middle right"
    "footer footer footer";
  /* there are differences between 'auto' and '1fr'
     but they're minor, and your intent seems to be
     to use the entirety of the space available, so
     I chose to replace 'auto' with '1fr': */
  grid-template-columns: 15% 1fr 15%;
  /* using grid-template-rows to specify the heights
     of the various rows: */
  grid-template-rows: min-content 1fr 50px;
  /* setting a margin on the block-axis of the element
     to 0 (this is implicit from the 'reset' above; but
     if it needs to be changed I like it to be explicitly
     stated for ease of changing) the 'block-axis' is
     top-to-bottom in English and left-to-right languages: */
  margin-block: 0;
  /* I put a margin of 20px on the inline-axis (the axis of
     writing-flow, left-to-right in English): */
  margin-inline: 20px;
  /* this makes the minimum height of the element 100 viewport-units
     tall, but allows it to expand if the content demands it: */
  min-height: 100vh;
}

#allTabsContainer > div {
  /* placing a border on all elements taking part in the grid: */
  border: 2px solid #ccc;
}

#header {
  /* positioning the grid-element in the named grid-area: */
  grid-area: header;
}

#header ul {
  /* using flex-layout to arrange the elements to flow in the
     inline axis: */
  display: flex;
  /* placing a 1em gap between adjacent elements: */
  gap: 1em;
  /* vertically aligning the child elements: */
  align-content: center;
  /* aligning the child elements to the start of the inline-flow
     (to the left in a left-to-right language such as English): */
  justify-content: start;
}

.tab-switcher {
  cursor: pointer;
  /* setting the defaults for the text-decoration
     (not using 'text-decoration' shorthand): */
  text-decoration-color: transparent;
  text-decoration-line: underline;
  text-decoration-thickness: 0.1em;
  /* transitioning all properties: */
  transition: all 0.3s linear;
}

/* selects all .tab-switcher elements when they match
   any of the supplied pseudo-classes listed in the :is()
   pseudo-class: */
.tab-switcher:is(:hover, :active, :focus) {
  color: rebeccapurple;
  text-decoration-color: currentColor;
  text-decoration-line: underline;
  text-decoration-thickness: 0.2em;
}

/* positioning elements in the named grid-areas: */
.tab-container.left {
  grid-area: left;
}
.tab-container.middle {
  grid-area: middle;
}

#right {
  grid-area: right;
}

#footer {
  background-color: #ccc;
  grid-area: footer;
  padding-inline: 1em;
  /* again using CSS logical properties, here we align the text
     to the end of the inline-axis (the right, in a left-to-right
     language such as English): */
  text-align: end;
}

/* here we select all element(s) which are not a '.tab-switcher'
   element which have a data-tab-index attribute and are a
   descendant of an element(s) with a data-tab-show attribute: */
[data-tab-show] [data-tab-index]:not(.tab-switcher) {
  /* we visually hide those elements (this may be imperfect for
     use in non-visual media, such as screen-readers; please check): */
  opacity: 0;
  visibility: hidden;
}

/* here we undo the hiding of those elements, by selecting elements which
   are not '.tab-switcher' elements, that have a data-tab-index
   attribute-value equal to 0 (or 1) which is a descendant of an element(s)
   that have a data-tab-show attribute-value of 0 (or 1): */
[data-tab-show="0"] [data-tab-index="0"]:not(.tab-switcher),
[data-tab-show="1"] [data-tab-index="1"]:not(.tab-switcher) {
  opacity: 1;
  visibility: visible;
}

button {
  /* adding padding to the <button> elements, purely for aesthetic purposes: */
  padding-block: 0.2em;
  padding-inline: 0.4em;
}
<!-- the grid display is now specified on the following element, and all elements taking part
     in the grid are now siblings; this is so that they're all 'aware' of the grid in their
     parent element and can be positioned in those areas.
     You'll note that there is a new data-* attribute, the "data-tab-show" attribute with a
     value set to 0 on page-load (this is modified in the JavaScript that handles the
     toggling: -->
<div id="allTabsContainer" data-tab-show="0">
  <!-- HEADER AND TABS -->
  <div id="header">
    <ul>
      <li  data-tab-index="0" tabindex="0">Collecte</li>
      <li  data-tab-index="1" tabindex="0">Journal</li>
    </ul>
  </div>

  <!-- CONTENT ZONE -->
  <!-- I've also removed the id for the #middle0, #middle1, #left0, #left1...
       elements, since it did nothing particularly useful and made it more
       awkward to position those elements; instead I added a class to make
       it easier ('middle' and 'left') to allow for positioning within
       the grid: -->
  <div  data-tab-index="0">
    <!-- TAB1 -->
    <button  id="But_wood">Pick wood</button>
    <button  id="But_tree">Cut a tree</button>
  </div>
  <div  data-tab-index="1">
    <!-- TAB2 -->
    <button  id="But_stone">Collect stone</button>
    <button  id="But_grass">Collect grass</button>
  </div>

  <div  data-tab-index="0">
    <p>Wood : <span id="Wood"></span></p>
  </div>

  <div  data-tab-index="1">
    <p>Stone : <span id="Stone"></span></p>
    <p>Grass : <span id="Grass"></span></p>
  </div>

  <!-- LOGS & CHAT SHOULD BE ALWAYS ACTIVE -->
  <div id="right">
    <p>Logs</p>
    <p id="log">...</p>
  </div>

  <!-- FOOTER SHOULD BE ALWAYS ACTIVE -->
  <div style="grid-area: footer" id="footer">
    <p>contenu relatif au site et à l'auteur</p>
  </div>

</div>

<!-- MISC. & SCRIPTS -->
<!-- commented out as it causes a GET error in the console on sites
     where this script doesn't exist (and while I'm looking at the
     console for errors it's distracting to have an expected error
     there:
  <script src="scripts/main.js"></script>
  -->

JS Fiddle demo.

References:

CodePudding user response:

Here I will concentrate on the layout using the grid with 4 areas.

I will then show how to handle clicks of the tabs and some actions related to that using a "target" data attribute. I will leave the style of color etc. to you but put borders on to illustrate each area.

$(function() {
  $(".tab-switcher").on('click', function(event) {
    let tabClicked = $(this).data("tabTarget");
    let lastTab = $(this).closest(".tab-switcher-list").data("last-clicked");
    $("#allTabsContainer .tab-container").hide().filter(tabClicked).show();
  }).first().trigger('click');
});
html {
  /* background-color: #aaaaaa; out just because I dislike gray for visually challenged people reasons */
  font-size: 16px;
  font-family: "alegreya", serif;
}

body {
  /* min-width: 550px; */
  margin: 0;
  padding-top: 0;
  padding-right: 1em;
  padding-left: 1em;
  padding-bottom: 1em;
  border: solid 1px lime;
}

.page-container {
  display: grid;
  grid-template: auto 1fr auto / auto 1fr auto;
}

.container-header {
  padding: 1rem;
  grid-column: 1 / 4;
}

.container-left {
  grid-column: 1 / 2;
}

.container-main {
  grid-column: 2 / 3;
}

.container-right {
  grid-column: 3 / 4;
}

.container-footer {
  grid-column: 1 / 4;
}

.container-header {
  border: solid red 1px;
}

.page-pane {
  /* base style for all panes */
  border-color: #CCCCCC;
  border-style: solid;
  border-top-width: 0.125em;
  border-left-width: 0.125em;
  border-bottom-width: 0.125em;
  border-right-width: 0.125em
}

.container-left {
  border-right-width: 0.0625em;
  border-color: cyan;
}

.container-main {
  min-height: 20rem;
}

.container-right {
  border-right-width: 0.0625em;
  border-color: magenta;
  padding-left: 0.5em;
}

.container-footer {
  grid-column: 1 / 4;
  text-align: right;
  align-self: end;
}

.tab-switcher-container {
  display: flex;
  justify-content: center;
  border: solid orange 0.0875em;
  height: 2.5rem;
}

nav .tab-switcher-list {
  display: flex;
  justify-content: center;
  list-style-type: none;
  border-bottom: 0.25em solid #444444;
  height: 1.25em;
}

.tab-switcher {
  flex: auto;
  margin: 0.25em;
  background-color: #EEEEEE;
  cursor: pointer;
  border: 1px solid #44ddCC;
  height: 1.25rem;
  padding-left: 0.25em;
  padding-right: 0.25em;
  margin: 0;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>

<div >
  <div >
    <div>I am the header pane</div>
    <nav >
      <ul class=tab-switcher-list>
        <li  data-tab-target=".tab-1">Collecte</li>
        <li  data-tab-target=".tab-2">Journal</li>
        <li  data-tab-target=".tab-cheese">Cheese</li>
      </ul>
    </nav>
  </div>
  <div >
    <div>I am the left pane</div>
    <div id="allTabsContainer">
      <div  data-tab-index="0">
        <div>
          <p>Wood:<span>I am wood, get wood</span></p>
        </div>
      </div>
      <div  data-tab-index="1">
        <div>
          <p>Stone:<span>I am stone, get stone</span></p>
          <p>Grass:<span>Grass I am - why am in in stone?!</span></p>
        </div>
      </div>
      <div  data-tab-index="1">
        <div>
          <p>Cheese:<span>I am cheese!</span></p>
          <p>Flavor:<span>Cheddrer Cheese!</span></p>
        </div>
      </div>
    </div>
  </div>
  <div >
    <div>I am the main pane</div>
    <button >Pick wood</button>
    <button >Cut a tree</button>
    <button >Collect stone</button>
    <button >Collect grass</button>
    <p>Here I am make me happy</p>
    <p>Here I am make me happy</p>
    <p>Here I am make me happy</p>
  </div>
  <div >
    <div>I am the right page pane</div>
    <p>Logs</p>
    <p id="log">...</p>
  </div>
  <div >
    <div>I am the footer pane</div>
    <div>
      <p>contenu relatif au site et à l'auteur</p>
    </div>
  </div>
</div>

  • Related