7

I'm trying to make the slides within jQuery UI tabs look as if they're next to each other and somewhat attached. I think I'll be able to achieve this by running the show and hide animations at the same time.

Currently jQuery slides the current panel out and then the next one. How can I slide out the current panel while at the same time slide in the next one?

$("#tabs").tabs({
hide:{effect:"slide", direction:"right"},
show:{effect:"slide", direction:"left"}
});

http://jsfiddle.net/CnEUh/2372/

I want to start both the hide: and show: effects at the same time rather than one after the other

  • I don't understand what you are asking for. If you want, you can use fadeIn / fadeOut effect if you don't want the sliding effect? – JonH Dec 07 '15 at 15:03
  • I'm trying to show and hide the tabs slides at the same time so that it looks like their next to each other. I don't want to use fade effects. –  Dec 07 '15 at 15:05
  • Note sure this is even possible given JQuery UI methodology. – Paulie_D Dec 07 '15 at 15:37
  • There has to be a way. –  Dec 07 '15 at 15:46
  • Seems like you'd be better off using a carousel, which does exactly that. You can style the controls as tabs if you like. – isherwood Dec 07 '15 at 18:26
  • I have thought of that but I would really prefer using jQuery tabs. –  Dec 07 '15 at 18:39
  • @Shabbb - It wont work the way you want it...you can want it all you want. – JonH Dec 07 '15 at 20:31
  • I'm hoping someone will help me find a solution, otherwise i'll have to leave it like that. But I'll still be using jquery tabs, or maybe theres a similar plugin that allows what I'm trying to do. –  Dec 07 '15 at 20:57
  • A simple hack solution would be to not place any content in the tab body. Place your content underneath, then react to the tab event change to slide your content yourself. It's not difficult to make your own carousel-like like slider. Just place all the content next to each other and animate the margin-left. – Lee Taylor Dec 09 '15 at 15:09
  • Can you provide an example? –  Dec 09 '15 at 15:15
  • @Shabb - Example is there now... – Lee Taylor Dec 09 '15 at 17:22

5 Answers5

5

Here's the version based on my original comment.

This keeps jQuery's tab system, but hides the existing tabs. New slidingTabs div contains the sliding tabs so they can be animated.

Update

As per request, the initial content remains as it was before.

function makeTabsIntoSlidingTabs($tabs) {
  $tabs.find("div").wrapAll("<div style='display:none' />");
  $tabs.append("<div class='slidingTabs' />");
  $tabs.children("div").first().find("div").each(function(i) {
    $tabs.find(".slidingTabs").append($("<div />").addClass("tab").html($(this).html()));
  });

  $tabs.tabs({
    activate: function(event, ui) {
      var tab = $tabs.tabs("option", "active");
      $tabs.find(".slidingTabs div").first().animate({
        marginLeft: (tab * -100) + '%'
      }, 400, function() {});
    }
  });
}

makeTabsIntoSlidingTabs($("#tabs"));
.slidingTabs {
  white-space: nowrap;
  overflow-x: hidden;
}
.slidingTabs .tab {
  width: 100%;
  display: inline-block;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>

<script src="http://code.jquery.com/ui/1.10.3/jquery-ui.js"></script>
<link href="http://ajax.googleapis.com/ajax/libs/jqueryui/1.8.18/themes/base/jquery-ui.css" rel="stylesheet" />


<div id="tabs">
  <ul>
    <li><a href="#tabs-1">Tab 1</a>
    </li>
    <li><a href="#tabs-2">Tab 2</a>
    </li>
    <li><a href="#tabs-3">Tab 3</a>
    </li>
  </ul>
  <div id="tabs-1">
    <p>Content for Tab 1</p>
  </div>
  <div id="tabs-2">
    <p>Content for Tab 2</p>
  </div>
  <div id="tabs-3">
    <p>Content for Tab 3</p>
  </div>
</div>
Lee Taylor
  • 7,761
  • 16
  • 33
  • 49
  • Theres a little spacing between each slide. I tried replacing display:inline-block with float:left but that breaks it. –  Dec 09 '15 at 18:37
  • @Shabb - Fixed. The reason was because of white space between each div. As long as there is no gap between one div ending and the other starting then there will be no gap. – Lee Taylor Dec 09 '15 at 18:49
  • Seems like a good solution, although I'll see if there are better methods mainly because I don't want to change the HTML markup. –  Dec 09 '15 at 21:08
  • @Shabb - New version which negates the need to modify the original content. – Lee Taylor Dec 09 '15 at 21:37
3

I think the proper way to change the behavior of a jQuery's widget is to extends it.

This way the solution is extensible, leave room for customization while allowing the tabs to remain a ui.widget, preserving jQuery's styling and state control.

$(document).ready(function() {
  // Clean whitespaces before creating tabs
  $('#tabs').cleanWhitespace();

  /* retains most of $.ui.tabs options, but now since both
   * hide & show animations are combined there's a
   * shared direction & duration
   */
  $("#tabs").customSlideTabs({
    direction: "left",
    duration: 500
  });
});


$.widget("nameSpace.customSlideTabs", $.ui.tabs, {
  _toggle: function(event, eventData) {
    var that = this,
      toShow = eventData.newPanel,
      toHide = eventData.oldPanel;

    this.running = true;

    var container = $(toHide.parent());
    var originalContainerOverflow = container.css("overflow");

    function complete() {
      container.css("overflow", originalContainerOverflow);

      eventData.newTab.closest("li").addClass("ui-tabs-active ui-state-active");
      eventData.oldTab.closest("li").removeClass("ui-tabs-active ui-state-active");

      that.running = false;
      that._trigger("activate", event, eventData);
    }

    // start out by hiding, then showing, then completing
    if (toHide.length && toShow.length) {
      if (!this.options.duration) this.options.duration = 300;

      container.css({
        "overflow": "hidden",
        "white-space": "nowrap"
      });

      var fromX, toX;

      if (this.options.direction == "right") {
        toHide.appendTo(container);
        fromX = "-100%";
        toX = 0;
      } else {
        toShow.appendTo(container);
        fromX = 0;
        toX = "-100%";
      }

      toShow.css({
        "width": "100%",
        "box-sizing": "border-box",
        "display": "inline-block",
        "vertical-align": "top",
        "position": "relative",
        "left": fromX,
        "white-space": "wrap"
      });
      toHide.css({
        "width": "100%",
        "box-sizing": "border-box",
        "display": "inline-block",
        "vertical-align": "top",
        "position": "relative",
        "left": fromX,
        "white-space": "wrap"
      });

      toShow.animate({
        "left": toX
      }, {
        duration: that.options.duration,
        complete: function() {
          toShow.attr("style", "display: block;");
        }
      });

      toHide.animate({
        "left": toX
      }, {
        duration: that.options.duration,
        complete: function() {
          toHide.attr("style", "display: none;");
          complete();
        }
      });
    } else {
      toHide.hide();
      toShow.show();
      complete();
    }

    toHide.attr({
      "aria-expanded": "false",
      "aria-hidden": "true"
    });
    eventData.oldTab.attr("aria-selected", "false");
    // If we're switching tabs, remove the old tab from the tab order.
    // If we're opening from collapsed state, remove the previous tab from the tab order.
    // If we're collapsing, then keep the collapsing tab in the tab order.
    if (toShow.length && toHide.length) {
      eventData.oldTab.attr("tabIndex", -1);
    } else if (toShow.length) {
      this.tabs.filter(function() {
          return $(this).attr("tabIndex") === 0;
        })
        .attr("tabIndex", -1);
    }

    toShow.attr({
      "aria-expanded": "true",
      "aria-hidden": "false"
    });
    eventData.newTab.attr({
      "aria-selected": "true",
      tabIndex: 0
    });
  }
});

// Gratitues http://stackoverflow.com/a/2587356/1645830
$.fn.cleanWhitespace = function() {
  textNodes = this.contents().filter(
      function() {
        return (this.nodeType == 3 && !/\S/.test(this.nodeValue));
      })
    .remove();
  return this;
}
<link href="http://ajax.googleapis.com/ajax/libs/jqueryui/1.8.18/themes/base/jquery-ui.css" rel="stylesheet" />
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="http://code.jquery.com/ui/1.10.3/jquery-ui.js"></script>

<div id="tabs">
  <ul>
    <li><a href="#tabs-1">Tab 1</a>
    </li>
    <li><a href="#tabs-2">Tab 2</a>
    </li>
    <li><a href="#tabs-3">Tab 3</a>
    </li>
  </ul>
  <div id="tabs-1" class="tab">
    <p>Content for Tab 1</p>
  </div>
  <div id="tabs-2" class="tab">
    <p>Content for Tab 2</p>
    <p>Content for Tab 2</p>
  </div>
  <div id="tabs-3" class="tab">
    <p>Content for Tab 3</p>
  </div>
</div>
<div id="tabid"></div>

This is by no mean complete, since it only support sliding left & right and not the full set of animations from jQuery, but I think it's a good base to work on.

AVAVT
  • 7,058
  • 2
  • 21
  • 44
  • 1
    It would have been a 20 lines JS code w/o jQuery's tabs + some styles in css. Your JS code beast is just too much for handling such a tiny component as tabs. – knitevision Dec 10 '15 at 10:22
  • Well, I'm only answering the question that the OP asked. He stated clearly that he want to keep using jQuery tabs, and I showed the best solution in my knowledge: using an approach advised by jQuery. And if you think a tabs functionality similar to jQuery's is 20 lines of js and "some" CSS you're either a front-end god or you clearly don't know what you're talking about. – AVAVT Dec 10 '15 at 12:34
  • 1
    Well. There's no need to be a god here. Really simple architectural solution with JS managing classes and CSS managing the transitions. – knitevision Dec 10 '15 at 18:32
  • Well, this off topic conversation went on long enough. I've already proposed my solution like everyone else. You're welcome to give yours, too. – AVAVT Dec 10 '15 at 18:38
3

Try this out .Hope this is what you needed.i have used the css transition for the slide effect.

$(document).ready(function() {
$("#tabs").tabs({
  beforeActivate: function(event, ui) {
    var index = ui.newTab.find('a').attr('href');
    var currentPage = $(index).index();
    //530 is the width of the frame or you can say the overall width including padding and margin for the content tabs.
    $('.inner').css('left', '-' + (currentPage) * 530 + 'px');
  },
});
 
});
body {
  background-color: #eef;
}

#tabs {
  width: 95%;
  margin-left: auto;
  margin-right: auto;
  margin-top: 10px;
}

#maindiv {
  position: relative;
  overflow: hidden;
  width: 530px;
}

.inner {
  position: relative;
  display: inline-flex;
  height: 100%;
  transition: -webkit-transition: left .6s ease-in-out;
  transition: left .6s ease-in-out;
}

#tabs-1,
#tabs-2,
#tabs-3 {
  outline: solid 5px red;
  outline-offset: -5px;
  float: left;
  display: block !important;
  width: 480px;
}
<link href="http://ajax.googleapis.com/ajax/libs/jqueryui/1.8.18/themes/base/jquery-ui.css" rel="stylesheet" />
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="http://code.jquery.com/ui/1.10.3/jquery-ui.js"></script>

<div id="tabs">
  <ul>
    <li><a href="#tabs-1">Tab 1</a>
    </li>
    <li><a href="#tabs-2">Tab 2</a>
    </li>
    <li><a href="#tabs-3">Tab 3</a>
    </li>
  </ul>
  <div id="maindiv">
    <div class="inner" style="left:0px;">
      <div id="tabs-1">
        <p>Content for Tab 1</p>
      </div>
      <div id="tabs-2">
        <p>Content for Tab 2</p>
      </div>
      <div id="tabs-3">
        <p>Content for Tab 3</p>
      </div>
    </div>
  </div>
</div>
<div id="tabid"></div>

you can check the js fiddle Here.

nitish koundade
  • 801
  • 5
  • 12
1

This is NOT what you may be looking for, but you can always look for other solutions outside what jqueryui libraries gives you.

in this example I have removed the slide effect, I have wrapped your tabcontent divs into a container with nowrap and displayed them as inline-block so they are standing side by side even if you don't see them (overflow:hidden at the parent), then with little jquery adding and removing classes to this new container when you click on the tabs and a css transition you may have what you are looking for.

Ugly as hell imho but it's an example of a work around

$("#tabs").tabs({
    hide:{

  },
    show:{

  }
});
$('#ui-id-1').click(function() {
    $('.container').removeClass("move1");
    $('.container').removeClass("move2");
});
$('#ui-id-2').click(function() {
    $('.container').addClass("move1");
    $('.container').removeClass("move2");    
});
$('#ui-id-3').click(function() {
    $('.container').addClass("move2");
}); 

JSFIDDLE

Alvaro Menéndez
  • 8,766
  • 3
  • 37
  • 57
  • It looks like it works but the method seems weird to me. Moving the slides rather than sliding them out is not what I'm looking for. Thank you. –  Dec 09 '15 at 17:12
1

It's not possible to achieve this effect, without changing tabs js script. You can use a workaround with beforeActivate method. Eg.:

$("#tabs").tabs({
    hide:{effect:"slide", direction:"right"},
    beforeActivate: function( event, ui ){ui.newPanel.show("slide", { direction: "left" });}
});

But then there's a problem with tabs positioning. I written quick workaround for this problem, but i think it's better solution to use some custom js. jsFiddle

$("#tabs").tabs({
  hide: {
    effect: "slide",
    direction: "right"
  },
  beforeActivate: function(event, ui) {
    setTimeout(function() {
      ui.newPanel.css({
        'position': 'absolute',
        'width': '100%',
        'top': ui.oldPanel.offset().top - 11
      });
      ui.newPanel.show("slide", {
        direction: "left"
      }, function() {
        ui.newPanel.css({
          'position': 'relative',
          'width': 'auto',
          'top': 0
        });
      });
    }, 15)
  }
});
body {
  background-color: #eef;
}
#tabs {
  width: 95%;
  margin-left: auto;
  margin-right: auto;
  margin-top: 10px;
  position: relative;
  overflow: hidden;
}
#tabs-1,
#tabs-2,
#tabs-3 {
  outline: solid 5px red;
  outline-offset: -5px;
}
<link href="http://ajax.googleapis.com/ajax/libs/jqueryui/1.8.18/themes/base/jquery-ui.css" rel="stylesheet" />
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.0/jquery.min.js"></script>
<script src="http://code.jquery.com/ui/1.10.3/jquery-ui.js"></script>
<div id="tabs">
  <ul>
    <li><a href="#tabs-1">Tab 1</a>
    </li>
    <li><a href="#tabs-2">Tab 2</a>
    </li>
    <li><a href="#tabs-3">Tab 3</a>
    </li>
  </ul>
  <div id="tabs-1">
    <p>Content for Tab 1
      <br/>
    </p>
  </div>
  <div id="tabs-2">
    <p>Content for Tab 2</p>
  </div>
  <div id="tabs-3">
    <p>Content for Tab 3</p>
  </div>
</div>
<div id="tabid"></div>
Gvidas
  • 1,964
  • 2
  • 14
  • 21