Scrollable tabs that can handle overflow correctly

I have the snippet as below. In mobile screen which is small ,it will not able to shows all 7 tabs at once. So, it will display as two rows of tabs which is not tidy.

I want the tabs that when scroll the page to the end of the right or left, it can handle overflow so that user will notice there are tabs there.

Example is the scroollable tabs for android in java. However, I need it in ionic 1 . Let said the screen in mobile able to shows 3 tabs out of 7 tabs. When I scroll the tabs to the right(the 3rd tab), the 4th tab will shows at the right, the 3rd tab will be on the middle tab now, while the second tab on the right.

Please show the code in snippet so that we can visually know it is working.

angular.module('ionicApp', ['ionic'])

.controller('MyCtrl', function($scope,$ionicSlideBoxDelegate) {
 
    $scope.slideIndex = 0;

            // Called each time the slide changes
        $scope.slideChanged = function(index) {
            $scope.slideIndex = index;
            

            

        };

        $scope.activeSlide = function (index) {
            $ionicSlideBoxDelegate.slide(index);
        };
});
body {
  cursor: url('https://ionicframework.com/img/finger.png'), auto;
}

.slide-tab{
    display: table;
    overflow: hidden;
    margin: 0;
    width: 100%;
    background-color: #eff0f2;
    border-bottom: 2px solid #00897B;

}

.slide-tab li{
    float: left;
    line-height: 38px;
    overflow: hidden;
    padding: 0;
}

.slide-tab a{
    background-color: #eff0f2;
    border-bottom: 1px solid #fff;
    color: #888;
    font-weight: 500;
    display: block;
    letter-spacing: 0;
    outline: none;
    padding: 0 20px;
    text-decoration: none;
    -webkit-transition: all 0.2s ease-in-out;
    -moz-transition: all 0.2s ease-in-out;
    transition: all 0.2s ease-in-out;
    border-bottom: 2px solid #00897B;
}
.current a{
    color: #fff;
    background: #00897B;
}
<html ng-app="ionicApp">
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="initial-scale=1, maximum-scale=1, user-scalable=no, width=device-width">

    <title>Ionic Slide Tab</title>

    <link href="//code.ionicframework.com/nightly/css/ionic.css" rel="stylesheet">
    <script src="//code.ionicframework.com/nightly/js/ionic.bundle.js"></script>
    
  </head>
  <body ng-controller="MyCtrl">

    <ion-header-bar class="bar-positive">
      <h1 class="title">Ionic Slide Tab</h1>
    </ion-header-bar>

    <ion-content>
      <div class="row">
        <ul class="slide-tab">
            <li ng-class="slideIndex == 0 ? 'current':''"><a href="#" ng-click="activeSlide(0)">Tab01</a></li>
            <li ng-class="slideIndex == 1 ? 'current':''"><a href="#" ng-click="activeSlide(1)">Tab02</a></li>
            <li ng-class="slideIndex == 2 ? 'current':''"><a href="#" ng-click="activeSlide(2)">Tab03</a></li>
            <li ng-class="slideIndex == 3 ? 'current':''"><a href="#" ng-click="activeSlide(3)">Tab04</a>
          
          </li>
          <li ng-class="slideIndex == 4 ? 'current':''"><a href="#" ng-click="activeSlide(4)">Tab05</a></li>
          <li ng-class="slideIndex == 5 ? 'current':''"><a href="#" ng-click="activeSlide(5)">Tab06</a></li>
          <li ng-class="slideIndex == 6 ? 'current':''"><a href="#" ng-click="activeSlide(6)">Tab07</a></li>
 
        </ul>
        </div>
        <ion-slide-box on-slide-changed="slideChanged(index)" active-slide="slideIndex" class="padding">
            <ion-slide>
                <h3>Tab 1</h3>
              <p>Page 1</p>
            </ion-slide>
            <ion-slide>
                <h3>Tab 2</h3>
               <p>Page 2</p>
            </ion-slide>
            <ion-slide>
                <h3>Tab 3</h3>
               <p>Page 3</p>
            </ion-slide>
            <ion-slide>
                <h3>Tab 4</h3>
               <p>page 4</p>
            </ion-slide>
           <ion-slide>
                <h3>Tab 5</h3>
               <p>page 5</p>
            </ion-slide>
           <ion-slide>
                <h3>Tab 6</h3>
               <p>page 6</p>
            </ion-slide>
           <ion-slide>
                <h3>Tab 7</h3>
               <p>page 7</p>
            </ion-slide>
        </ion-slide-box>
    </ion-content>

  </body>
</html>

try this example I think it will help you..

slidingTabs.html

<html>
  <head>
    <meta charset="utf-8" />
    <meta name="format-detection" content="telephone=no" />
    <meta name="viewport" content="initial-scale=1, maximum-scale=1, user-scalable=no, width=device-width">
    <title>Tabbed Slide box</title>

    <!-- ionic css -->
    <link href="http://code.ionicframework.com/1.0.0-rc.4/css/ionic.css" rel="stylesheet">

    <!-- your app's css -->
    <link href="tabSlideBox.css" rel="stylesheet">

    <!-- ionic/angularjs scripts -->
    <script src="http://code.ionicframework.com/1.0.0-rc.4/js/ionic.bundle.min.js"></script>

    <script src="tabSlideBox.js"></script>

    <!-- your app's script -->
    <script>
        var app = angular.module('slidebox', ['ionic', 'tabSlideBox'])
        .run(['$q', '$http', '$rootScope', '$location', '$window', '$timeout', 
                    function($q, $http, $rootScope, $location, $window, $timeout){

            $rootScope.$on("$locationChangeStart", function(event, next, current){
                $rootScope.error = null;
                console.log("Route change!!!", $location.path());
                var path = $location.path();


                console.log("App Loaded!!!");
            });
        }
        ]);

        app.config(function($stateProvider, $urlRouterProvider) {
            $stateProvider.state('index', {
                url : '/',
                templateUrl : 'index.html',
                controller : 'IndexCtrl'
            });

            $urlRouterProvider.otherwise("/");
        });

        app.controller("IndexCtrl", ['$rootScope', "$scope", "$stateParams", "$q", "$location", "$window", '$timeout', 
            function($rootScope, $scope, $stateParams, $q, $location, $window, $timeout){
            $scope.onSlideMove = function(data){
                alert("You have selected " + data.index + " tab");
            };
        }
        ]);
    </script>

    <style>
        .slider-slide h3{
            color:#fff;
            margin-top:10px;
        }
        .scroll{
            height:100%;
        }

        .tabbed-slidebox .tsb-icons:after{
            display:none;
        }
    </style>

  </head>

  <body ng-app="slidebox" animation="slide-left-right-ios7">

    <ion-nav-bar class="nav-title-slide-ios7 bar-positive">
        <ion-nav-back-button class="button-icon ion-arrow-left-c">
        </ion-nav-back-button>
    </ion-nav-bar>

    <ion-nav-view ng-controller="IndexCtrl"></ion-nav-view>

    <script id="index.html" type="text/ng-template">
      <ion-view title="Scrollable Tabbed Slide box">
        <ion-content scroll="false">
            <tab-slide-box>
                    <div class="tsb-icons">
                        <div class="tsb-ic-wrp">
                            <ion-scroll direction="x" class="tsb-hscroll">
                            <a href="javascript:;" class="ion-home1">Home</a>
                            <a href="javascript:;" class="ion-ios-game-controller-b1">Games</a>
                            <a href="javascript:;" class="ion-email1">Email</a>
                            <a href="javascript:;" class="ion-model-s1">Car</a>
                            <a href="javascript:;" class="ion-person1">Profile</a>
                            <a href="javascript:;" class="ion-heart1">Favourites</a>
                            <a href="javascript:;" class="ion-chatbubbles1">Chats</a>
                            <a href="javascript:;" class="ion-gear-b1">Settings</a>
                            <a href="javascript:;" class="ion-camera1">Photos</a>
                            <a href="javascript:;" class="ion-ios-paw1">Pets</a>
                            </ion-scroll>
                        </div>
                    </div>
                    <ion-slide-box show-pager="false" on-slide-changed="slideHasChanged($index)">
                        <ion-slide>
                            <h3>Home content</h3>
                        </ion-slide>
                        <ion-slide>
                            <h3>Games content</h3>
                        </ion-slide>
                        <ion-slide>
                            <h3>Mail content</h3>
                        </ion-slide>
                        <ion-slide>
                            <h3>Car content</h3>
                        </ion-slide>
                        <ion-slide>
                            <h3>Profile content</h3>
                        </ion-slide>
                        <ion-slide>
                            <h3>Favourites content</h3>
                        </ion-slide>
                        <ion-slide>
                            <h3>Chats content</h3>
                        </ion-slide>
                        <ion-slide>
                            <h3>Settings content</h3>
                        </ion-slide>
                        <ion-slide>
                            <h3>Photos content</h3>
                        </ion-slide>
                        <ion-slide>
                            <h3>Pets content</h3>
                        </ion-slide>
                    </ion-slide-box>
            </tab-slide-box>
        </ion-content>
      </ion-view>
    </script>
  </body>
</html>

tabSlideBox.css

.tabbed-slidebox .slider {
    height: 100%;
}
.tabbed-slidebox.btm .slider {
    margin-bottom: -50px;
}
.tabbed-slidebox .slider-slides {
    width: 100%;
}

.tabbed-slidebox .slider-slide {
    padding-top: 0px;
    color: #000;
    background-color: #fff;
    text-align: center;
    font-weight: 300;
    background-color: #0398dc;
}

.tabbed-slidebox .tsb-icons {
    text-align: center;
    margin: 10px 0;
    position: relative;
    background-color:#fff;
}

.tabbed-slidebox .tsb-ic-wrp {
    display: flex;
    position: relative;
    -webkit-transition: -webkit-transform 0.3s; /* For Safari 3.1 to 6.0 */
    -webkit-transform:translate3d(0,0,0);
}

.tabbed-slidebox .tsb-icons a {
    display: inline-block;
    width: 54px;
    font-size: 2.5em;
    color: rgba(0, 0, 0, 0.3);
    -webkit-transform:translate3d(0,8px,0);
}

.tabbed-slidebox .tsb-icons a.active {
    color: rgba(0, 0, 0, 1);
    font-size: 3.5em;
    -webkit-transform:translate3d(0,0,0);
}

.tabbed-slidebox .tabbed-slidebox {
    position: relative;
    height: 100%;
    overflow: hidden;
}

.tabbed-slidebox .tsb-icons:after {
    width: 0;
    height: 0;
    border-style: solid;
    border-width: 0 1.4em 1.4em 1.4em;
    border-color: transparent transparent #0398dc transparent;
    position: absolute;
    content: "";
    display: block;
    bottom: -12px;
    left: 50%;
    margin-left: -14px;
}
.tsb-hscroll.scroll-view{
    white-space: nowrap;
    margin:0 auto;
}
.tsb-hscroll.scroll-view .scroll-bar{
    visibility:hidden;
}
.tsb-hscroll.scroll-view .scroll.onscroll{
    -webkit-transition: -webkit-transform 0.3s; /* For Safari 3.1 to 6.0 */
}

.tabbed-slidebox .tsb-icons .scroll a{
    width:auto;
    font-size: 1.5em;
    line-height: 1.5em;
    text-decoration: none;
    margin: 0 15px;
    border-bottom: 3px solid transparent;
}
.tabbed-slidebox .tsb-icons .scroll a.active {
    font-size: 1.8em;
    border-bottom: 3px solid #ccc;
}

tabSlideBox.js

'use strict';

function SimplePubSub() {
    var events = {};
    return {
        on: function(names, handler) {
            names.split(' ').forEach(function(name) {
                if (!events[name]) {
                    events[name] = [];
                }
                events[name].push(handler);
            });
            return this;
        },
        trigger: function(name, args) {
            angular.forEach(events[name], function(handler) {
                handler.call(null, args);
            });
            return this;
        }
    };
};

angular.module('tabSlideBox', [])
.directive('onFinishRender', function ($timeout) {
    return {
        restrict: 'A',
        link: function (scope, element, attr) {
            if (scope.$last === true) {
                $timeout(function () {
                    scope.$emit('ngRepeatFinished');
                });
            }
        }
    }
})
.directive('tabSlideBox', [ '$timeout', '$window', '$ionicSlideBoxDelegate', '$ionicScrollDelegate',
    function($timeout, $window, $ionicSlideBoxDelegate, $ionicScrollDelegate) {
        'use strict';

        return {
            restrict : 'A, E, C',
            link : function(scope, element, attrs, ngModel) {

                var ta = element[0], $ta = element;
                $ta.addClass("tabbed-slidebox");
                if(attrs.tabsPosition === "bottom"){
                    $ta.addClass("btm");
                }

                //Handle multiple slide/scroll boxes
                var handle = ta.querySelector('.slider').getAttribute('delegate-handle');

                var ionicSlideBoxDelegate = $ionicSlideBoxDelegate;
                if(handle){
                    ionicSlideBoxDelegate = ionicSlideBoxDelegate.$getByHandle(handle);
                }

                var ionicScrollDelegate = $ionicScrollDelegate;
                if(handle){
                    ionicScrollDelegate = ionicScrollDelegate.$getByHandle(handle);
                }

                function renderScrollableTabs(){
                    var iconsDiv = angular.element(ta.querySelector(".tsb-icons")), icons = iconsDiv.find("a"), wrap = iconsDiv[0].querySelector(".tsb-ic-wrp"), totalTabs = icons.length;
                    var scrollDiv = wrap.querySelector(".scroll");

                    angular.forEach(icons, function(value, key){
                         var a = angular.element(value);
                         a.on('click', function(){
                             ionicSlideBoxDelegate.slide(key);
                         });

                        if(a.attr('icon-off')) {
                            a.attr("class", a.attr('icon-off'));
                        }
                    });

                    var initialIndex = attrs.tab;
                    //Initializing the middle tab
                    if(typeof attrs.tab === 'undefined' || (totalTabs <= initialIndex) || initialIndex < 0){
                        initialIndex = Math.floor(icons.length/2);
                    }

                    //If initial element is 0, set position of the tab to 0th tab 
                    if(initialIndex == 0){
                        setPosition(0);
                    }

                    $timeout(function() {
                        ionicSlideBoxDelegate.slide(initialIndex);
                    }, 0);
                }
                function setPosition(index){
                    var iconsDiv = angular.element(ta.querySelector(".tsb-icons")), icons = iconsDiv.find("a"), wrap = iconsDiv[0].querySelector(".tsb-ic-wrp"), totalTabs = icons.length;
                    var scrollDiv = wrap.querySelector(".scroll");

                    var middle = iconsDiv[0].offsetWidth/2;
                    var curEl = angular.element(icons[index]);
                    var prvEl = angular.element(iconsDiv[0].querySelector(".active"));
                    if(curEl && curEl.length){
                    var curElWidth = curEl[0].offsetWidth, curElLeft = curEl[0].offsetLeft;

                    if(prvEl.attr('icon-off')) {
                        prvEl.attr("class", prvEl.attr('icon-off'));
                    }else{
                        prvEl.removeClass("active");
                    }
                    if(curEl.attr('icon-on')) {
                        curEl.attr("class", curEl.attr('icon-on'));
                    }
                    curEl.addClass("active");

                    var leftStr = (middle  - (curElLeft) -  curElWidth/2 + 5);
                    //If tabs are not scrollable
                    if(!scrollDiv){
                        var leftStr = (middle  - (curElLeft) -  curElWidth/2 + 5) + "px";
                        wrap.style.webkitTransform =  "translate3d("+leftStr+",0,0)" ;
                    }else{
                        //If scrollable tabs
                        var wrapWidth = wrap.offsetWidth;
                        var currentX = Math.abs(getX(scrollDiv.style.webkitTransform));
                        var leftOffset = 100;
                        var elementOffset = 40;
                        //If tabs are reaching right end or left end
                        if(((currentX + wrapWidth) < (curElLeft + curElWidth + elementOffset)) || (currentX > (curElLeft - leftOffset))){
                            if(leftStr > 0){
                                leftStr = 0;
                            }
                            //Use this scrollTo, so when scrolling tab manually will not flicker
                            ionicScrollDelegate.scrollTo(Math.abs(leftStr), 0, true);
                        }
                    }
                    }
                };
                function getX(matrix) {
                    matrix = matrix.replace("translate3d(","");
                    matrix = matrix.replace("translate(","");
                    return (parseInt(matrix));
                }
                var events = scope.events;
                events.on('slideChange', function(data){
                    setPosition(data.index);
                });
                events.on('ngRepeatFinished', function(ngRepeatFinishedEvent) {
                    renderScrollableTabs();
                });

                renderScrollableTabs();
            },
            controller : function($scope, $attrs, $element) {
                $scope.events = new SimplePubSub();

                $scope.slideHasChanged = function(index){
                    $scope.events.trigger("slideChange", {"index" : index});
                    $timeout(function(){if($scope.onSlideMove) $scope.onSlideMove({"index" : eval(index)});},100);
                };

                $scope.$on('ngRepeatFinished', function(ngRepeatFinishedEvent) {
                    $scope.events.trigger("ngRepeatFinished", {"event" : ngRepeatFinishedEvent});
                });
            }
        };

    } 
]);

also check this example link to get more idea

Working plunker

链接地址: http://www.djcxy.com/p/39706.html

上一篇: Spring @Transactional提交失败; Deby + Eclipselink

下一篇: 可滚动的选项卡可以正确处理溢出