horizontal scrollbar on top and bottom of table
Asked Answered
N

22

216

I've a very large table on my page. So I decided to put a horizontal scrollbar on the bottom of the table. But I would like this scrollbar to be also on top on the table.

What I have in the template is this:

<div style="overflow:auto; width:100%; height:130%">
<table id="data" style="width:100%">...</table>
</div>

Is this possible to do in HTML and CSS only?

Nelly answered 14/10, 2010 at 14:32 Comment(5)
#2275127 I found this solution useful.Prawn
Yes. #18998224Sadoff
Modern suggestion: Make a "remote-control-scrollbar" component in your preferred framework (React, Vue, etc). You feed it one prop, the id of scrollable content, for example: <MyScrollbar :sourceTarget="myOrigScrollableArea"> and within the component you monitor the target and make appropriate changes to the custom scrollbar. Then whatever happens in the original scrollable area is dynamically reflected in the scrollbar component. Now you can copy-paste that component instance and create 100 scroll bars if you wish, in any orientation.Jessie
@VARSHADAS, the user is asking if it's possible in HTML and CSS only.Hierarchize
@SpencerMay, the user is asking for a scroll bar on both top and bottom of the table.Hierarchize
M
269

To simulate a second horizontal scrollbar on top of an element, put a "dummy" div above the element that has horizontal scrolling, just high enough for a scrollbar. Then attach handlers of the "scroll" event for the dummy element and the real element, to get the other element in synch when either scrollbar is moved. The dummy element will look like a second horizontal scrollbar above the real element.

For a live example, see this fiddle

Here's the code:

HTML:

<div class="wrapper1">
  <div class="div1"></div>
</div>
<div class="wrapper2">
  <div class="div2">
    <!-- Content Here -->
  </div>
</div>

CSS:

.wrapper1, .wrapper2 {
  width: 300px;
  overflow-x: scroll;
  overflow-y:hidden;
}

.wrapper1 {height: 20px; }
.wrapper2 {height: 200px; }

.div1 {
  width:1000px;
  height: 20px;
}

.div2 {
  width:1000px;
  height: 200px;
  background-color: #88FF88;
  overflow: auto;
}

JS:

$(function(){
  $(".wrapper1").scroll(function(){
    $(".wrapper2").scrollLeft($(".wrapper1").scrollLeft());
  });
  $(".wrapper2").scroll(function(){
    $(".wrapper1").scrollLeft($(".wrapper2").scrollLeft());
  });
});
Moulden answered 14/10, 2010 at 16:4 Comment(10)
$ is for jQuery. Just load it in the <head> of your page like this: <script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.4/jquery.min.js"></script>Piquant
Did you get it to work? For more on jQuery, see api.jquery.com/scrollLeft (Of course, you can do it without jQuery by attaching onscroll handlers directly.) For graceful degradation for users with no JS, you can add the dummy div to the DOM by JS.Moulden
yes I got it to work, I had only add <script type="text/javascript" src="path"></script> to <head>. So everytime I add jquery I had to add the one that @fudgey add? Sorry, javascript and jquery are still kind of chinese to meNelly
You really shouldn't add javascript to the head of the html file. Want to know why? robertnyman.com/2008/04/23/…Elamitic
@Moulden : how to set the div to automatically take the width of the table inside it ? i don't want to specify the width of div2 manually, but want it to automatically take the width of table which it contains.Myrilla
@CodyGuldner : how to set the div to automatically take the width of the table inside it ? i don't want to specify the width of div2 manually, but want it to automatically take the width of table which it contains.Myrilla
when load dynamic data mean on click fill grid the upper part become use less only bottom scroll works why can you fix thisInfra
Great solution! Note that inside the scroll() method, you can reference the scrollLeft value with this.scrollLeft, i.e. $(".wrapper1").scroll(function(){$(".wrapper2").scrollLeft(this.scrollLeft);});Remise
In MacOS Sierra, in Chrome and Safari, I can't grab the scrollbar and drag it. Is there a fix for this? But it works in Firefox Quantum on MacOS. It works on Windows in Chrome, Firefox, IE, I can drag the scrollbars.Treatise
.wrapper1 scroll() calls .wrapper2 scroll(), then .wrapper2 scroll() calls .wrapper1 scroll(), then .wrapper1 scroll() calls .wrapper2 scroll() again, and then....hrm, is this not an infinite loop?Anglophile
M
79

Solution only using CSS

There is one way to achieve this that I did not see anybody mentioning here.

By rotating the parent container by 180 degrees and the child-container again by 180 degrees the scrollbar will be shown at top

.parent {
  transform: rotateX(180deg);
  overflow-x: auto;
} 
.child {
  transform: rotateX(180deg);
}

For reference see the issue in the w3c repository.

Menhir answered 20/8, 2020 at 14:54 Comment(4)
it's a really nice solution if you only have a horizontal scroll, but if you have a vertical too, the vertical scroll is backwards (drag up to scroll down etc.).Lyso
The question is how to have scrollbar on top and bottom, both at the same time. But nice solution tho.Stun
In this way only top scrollbar is shown...the question ask for bothMuttonchops
I like this, if you want the vertical scrollbar to remain on the right you can flip the divs on the X axis instead of rotating - transform: rotateX(180deg); - see fiddle here: jsfiddle.net/5Diraptor/yoq2k3m6/11Magnifico
E
45

Try using the jquery.doubleScroll plugin :

jQuery :

$(document).ready(function(){
  $('#double-scroll').doubleScroll();
});

CSS :

#double-scroll{
  width: 400px;
}

HTML :

<div id="double-scroll">
  <table id="very-wide-element">
    <tbody>
      <tr>
        <td></td>
      </tr>
    </tbody>
  </table>
</div>
Endo answered 1/2, 2012 at 13:49 Comment(3)
See github.com/avianey/jqDoubleScroll for a version of Pawel's awesome plugin that does not require jQueryUI.Sofia
Link leads to a un-maintained GitHub with broken links in read me. Haven't tried the actual js.Strontian
Here's demo web.archive.org/web/20120221225342/http://suwala.eu/blog/2012/…Clarendon
P
42

Without JQuery (2017)

Because you might not need JQuery, here is a working Vanilla JS version based on @StanleyH answer:

var wrapper1 = document.getElementById('wrapper1');
var wrapper2 = document.getElementById('wrapper2');
wrapper1.onscroll = function() {
  wrapper2.scrollLeft = wrapper1.scrollLeft;
};
wrapper2.onscroll = function() {
  wrapper1.scrollLeft = wrapper2.scrollLeft;
};
#wrapper1, #wrapper2{width: 300px; border: none 0px RED;
overflow-x: scroll; overflow-y:hidden;}
#wrapper1{height: 20px; }
#wrapper2{height: 100px; }
#div1 {width:1000px; height: 20px; }
#div2 {width:1000px; height: 100px; background-color: #88FF88;
overflow: auto;}
<div id="wrapper1">
    <div id="div1">
    </div>
</div>
<div id="wrapper2">
    <div id="div2">
    aaaa bbbb cccc dddd aaaa bbbb cccc 
    dddd aaaa bbbb cccc dddd aaaa bbbb 
    cccc dddd aaaa bbbb cccc dddd aaaa 
    bbbb cccc dddd aaaa bbbb cccc dddd
    </div>
</div>
Poetize answered 31/10, 2017 at 14:42 Comment(3)
this actually isn't responsive/dynamic in the case you don't know your widthGamogenesis
yes! it has the same caveats as the original answer by @MouldenPoetize
@eladsilver Add a resize observer that watches for changes in wrapper2, and on changes sets the width of wrapper1 to be the same as wrapper2.Dukey
A
26

StanleyH's answer was excellent, but it had one unfortunate bug: clicking the shaded area of the scrollbar no longer jumps to the selection you click. Instead, what you get is a very small and somewhat annoying increment in the position of the scrollbar.

Tested: 4 versions of Firefox (100% affected), 4 versions of Chrome (50% affected).

Here's my jsfiddle. You can get around this with by having an on/off (true/false) var that allows only one onScroll() event to trigger at a time:

var scrolling = false;
$(".wrapper1").scroll(function(){
    if(scrolling) {
      scrolling = false;
      return true;
    }
    scrolling = true;
    $(".wrapper2")
        .scrollLeft($(".wrapper1").scrollLeft());
});
$(".wrapper2").scroll(function(){
    if(scrolling) {
      scrolling = false;
      return true;
    }
      scrolling = true;
    $(".wrapper1")
        .scrollLeft($(".wrapper2").scrollLeft());
});

Problem Behavior With Accepted Answer :

Actually Desired Behavior :

So, just why does this happen? If you run through the code, you'll see that wrapper1 calls wrapper2's scrollLeft, and wrapper2 calls wrapper1's scrollLeft, and repeat this infinitely, so, we have an infinite loop problem. Or, rather: the continued scrolling of the user conflicts with wrapperx's call of the scrolling, an event conflict occurs, and the end result is no jumping in the scrollbars.

Hope this helps someone else out!

Anglophile answered 30/5, 2019 at 18:55 Comment(1)
great explanation ! indeed it was doing an infinite loopCheroot
R
24

First of all, great answer, @StanleyH. If someone is wondering how to make the double scroll container with dynamic width :

css

.wrapper1, .wrapper2 { width: 100%; overflow-x: scroll; overflow-y: hidden; }
.wrapper1 { height: 20px; }
.div1 { height: 20px; }
.div2 { overflow: none; }

js

$(function () {
    $('.wrapper1').on('scroll', function (e) {
        $('.wrapper2').scrollLeft($('.wrapper1').scrollLeft());
    }); 
    $('.wrapper2').on('scroll', function (e) {
        $('.wrapper1').scrollLeft($('.wrapper2').scrollLeft());
    });
});
$(window).on('load', function (e) {
    $('.div1').width($('table').width());
    $('.div2').width($('table').width());
});

html

<div class="wrapper1">
    <div class="div1"></div>
</div>
<div class="wrapper2">
    <div class="div2">
        <table>
            <tbody>
                <tr>
                    <td>table cell</td>
                    <td>table cell</td>
                    <!-- ... -->
                    <td>table cell</td>
                    <td>table cell</td>
                </tr>
            </tbody>
        </table>
    </div>
</div>

demo

http://jsfiddle.net/simo/67xSL/

Routinize answered 11/6, 2014 at 20:1 Comment(4)
Use $('.div1').width( $('.div1')[0].scrollWidth) to get the real scroll width of the content, usefull when you are not specifically using a container like table. @Routinize Awesome effort mate.Margret
To clarify, I also had to change to code to scrollWidth in order for my own code to work where the width of the content within the "div2" div was variable due to server side dynamic content located there.Xanthene
This answer does not create a scrollbar at the top.Jd
there is no such thing as overflow:none; should be overflow: visible;Urbani
C
15

a javascript only solution that's based on @HoldOffHunger and @bobince answers

<div id="data">
  ...
</div>
function doubleScroll(element) {
    const scrollbar = document.createElement("div");
    scrollbar.appendChild(document.createElement("div"));
    scrollbar.style.overflow = "auto";
    scrollbar.style.overflowY = "hidden";
    scrollbar.firstChild.style.width = element.scrollWidth + "px";
    scrollbar.firstChild.style.paddingTop = "1px";
    scrollbar.firstChild.appendChild(document.createTextNode("\xA0"));
    let running = false;
    // Keep scrollbar in sync when element size changes
    new ResizeObserver(() => {
        scrollbar.firstChild.style.width = element.scrollWidth + "px";
    }).observe(element);
    scrollbar.onscroll = function () {
        if (running) {
            running = false;
            return;
        }
        running = true;
        element.scrollLeft = scrollbar.scrollLeft;
    };
    element.onscroll = function () {
        if (running) {
            running = false;
            return;
        }
        running = true;
        scrollbar.scrollLeft = element.scrollLeft;
    };
    element.parentNode.insertBefore(scrollbar, element);
}

doubleScroll(document.getElementById("data"));
Cheroot answered 9/7, 2019 at 12:46 Comment(2)
Nice work. In my case I also had to reduce the line-height on the element to remove extra space.Arrogance
More specifically, put scrollbar.firstChild.style.lineHeight = "0px"; after scrollbar.firstChild.style.paddingTop = "1px".Nonreturnable
C
11

You can use a jQuery plugin that will do the job for you :

The plugin will handle all the logic for you.

Cung answered 10/3, 2014 at 11:22 Comment(4)
Although this plugin does the same thing as suwala/jquery.doubleScroll plugin it has more options like recalculating width on window.resizeWhitcomb
Also does not depend on jquery UI.Marvel
I would recommend this plugin, it looks like an improved version of suwala/jquery.doubleScroll and works for meThinia
First thank you for the great plugin! However I have an issue with scroll direction: rtl. It seems it doesn't support it correctly. If any of you javascripts gurus knows how to fix it here is the fiddle: jsfiddle.net/qsn7tupc/3 Note that it works in Chrome but in no other browser.Virtu
S
7

React+TypeScript rap-2-h's code ported to TypeScript.

import React from 'react';
import { makeStyles } from '@material-ui/core/styles';
import { ReactNode } from 'react';
import { useRef } from 'react';

const useStyles = makeStyles((theme) => ({
  wrapper1: {
    width: '300px',
    height: '20px',
    overflowX: 'scroll',
    overflowY: 'hidden',
    border: 'none 0px black',
  },
  wrapper2: {
    width: '300px',
    height: '100px',
    overflowX: 'scroll',
    overflowY: 'hidden',
    border: 'none 0px black',
  },
  div1: {
    width: '1000px',
    height: '20px',
  },
  div2: {
    width: '1000px',
    height: '100px',
    backgroundColor: '#88FF88',
    overflow: 'auto',
  }
}));

export default function TopBottomScrollBars() {
  const classes = useStyles();
  const wrapRef1 = useRef<HTMLDivElement>(null);
  const wrapRef2 = useRef<HTMLDivElement>(null);

  const handleScroll: React.EventHandler<React.UIEvent<ReactNode>> = (event: React.UIEvent<React.ReactNode> ) => {
    const targetDiv: HTMLDivElement = event.target as HTMLDivElement;

    if(targetDiv === wrapRef1.current && wrapRef2.current) {
      wrapRef2.current.scrollLeft = targetDiv.scrollLeft;
    }
    else if(targetDiv === wrapRef2.current && wrapRef1.current) {
      wrapRef1.current.scrollLeft = targetDiv.scrollLeft;
    }
  };

  return (
    <div>
      <div ref={wrapRef1} className={classes.wrapper1} onScroll={handleScroll} >
        <div id="div1" className={classes.div1}>
      </div>
    </div>

    <div ref={wrapRef2} className={classes.wrapper2} onScroll={handleScroll}>
      <div id="div2" className={classes.div2}>
        aaaa bbbb cccc dddd aaaa bbbb cccc 
        dddd aaaa bbbb cccc dddd aaaa bbbb 
        cccc dddd aaaa bbbb cccc dddd aaaa 
        bbbb cccc dddd aaaa bbbb cccc dddd
      </div>
    </div>
    </div>
  );
}
Sewn answered 13/8, 2021 at 22:11 Comment(0)
P
5

Based on @StanleyH solution I created an AngularJS directive, demo on jsFiddle.

Easy to use:

<div data-double-scroll-bar-horizontal> {{content}} or static content </div>

For AngularJS developers

Pusillanimity answered 13/7, 2014 at 11:54 Comment(1)
"No jQuery required." Yeah, just a whole framework. No biggie.Chief
P
3

As far as I'm aware this isn't possible with HTML and CSS.

Popularize answered 14/10, 2010 at 14:40 Comment(7)
well this is actually what I thought.. But I would like to known if anyone has done this and the best way to do this..Nelly
For HTML and CSS, there is no best way. It is simply not possible. You need to use other techniques, like javascript, to get this kind of functionality.Keating
can you give some tips to start looking for?Nelly
@Pat I can't comment on @StanleyH's excellent answer for some reason so I'm responding here. The reason it says $ is not defined is probably because you haven't included the jQuery library in your document. That code needs this to function.Popularize
I've put at the beginning of the page <script type="text/javascript"></script>Nelly
This does not provide an answer to the question. To critique or request clarification from an author, leave a comment below their post.Roi
@Roi it does answer the question. The question was "Is this possible to do in HTML and CSS only?" and the answer to that is "no".Popularize
E
3

Linking the scrollers worked, but in the way it's written it creates a loop which makes scrolling slow in most browsers if you click on the part of the lighter scrollbar and hold it (not when dragging the scroller).

I fixed it with a flag:

$(function() {
    x = 1;
    $(".wrapper1").scroll(function() {
        if (x == 1) {
            x = 0;
            $(".wrapper2")
                .scrollLeft($(".wrapper1").scrollLeft());
        } else {
            x = 1;
        }
    });


    $(".wrapper2").scroll(function() {
        if (x == 1) {
            x = 0;
            $(".wrapper1")
                .scrollLeft($(".wrapper2").scrollLeft());
        } else {
            x = 1;
        }
    });
});
Ellissa answered 15/10, 2015 at 12:43 Comment(2)
This solves a problem I was having with the suggested answers here too. However, this only mitigates the issue slightly, there may still be stepped/slow scrolling if the distance you want to scroll is large enough for it.Brassica
With the conditional/flag this solution works well and tests match the behavior you would expect without JavaScript being applied in the browser.Normand
P
2

Expanding on StanleyH's answer, and trying to find the minimum required, here is what I implemented:

JavaScript (called once from somewhere like $(document).ready()):

function doubleScroll(){
        $(".topScrollVisible").scroll(function(){
            $(".tableWrapper")
                .scrollLeft($(".topScrollVisible").scrollLeft());
        });
        $(".tableWrapper").scroll(function(){
            $(".topScrollVisible")
                .scrollLeft($(".tableWrapper").scrollLeft());
        });
}

HTML (note that the widths will change the scroll bar length):

<div class="topScrollVisible" style="overflow-x:scroll">
    <div class="topScrollTableLength" style="width:1520px; height:20px">
    </div>
</div>
<div class="tableWrapper" style="overflow:auto; height:100%;">
    <table id="myTable" style="width:1470px" class="myTableClass">
...
    </table>

That's it.

Photokinesis answered 29/7, 2014 at 22:30 Comment(0)
E
2

In vanilla Javascript/Angular you can do this like this:

scroll() {
    let scroller = document.querySelector('.above-scroller');
    let table = document.querySelector('.table');
    table.scrollTo(scroller.scrollLeft,0);
  }

HTML:

<div class="above-scroller" (scroll)="scroll()">
  <div class="scroller"></div>
</div>
<div class="table" >
  <table></table>
</div>

CSS:

.above-scroller  {
   overflow-x: scroll;
   overflow-y:hidden;
   height: 20px;
   width: 1200px
 }

.scroller {
  width:4500px;
  height: 20px;
}

.table {
  width:100%;
  height: 100%;
  overflow: auto;
}
Edythedythe answered 9/6, 2018 at 15:51 Comment(0)
A
2

Here is an example for VueJS

index.page

<template>
  <div>
    <div ref="topScroll" class="top-scroll" @scroll.passive="handleScroll">
      <div
        :style="{
          width: `${contentWidth}px`,
          height: '12px'
        }"
      />
    </div>
    <div ref="content" class="content" @scroll.passive="handleScroll">
      <div
        :style="{
          width: `${contentWidth}px`
        }"
      >
        Lorem ipsum dolor sit amet, ipsum dictum vulputate molestie id magna,
        nunc laoreet maecenas, molestie ipsum donec lectus ut et sit, aut ut ut
        viverra vivamus mollis in, integer diam purus penatibus. Augue consequat
        quis phasellus non, congue tristique ac arcu cras ligula congue, elit
        hendrerit lectus faucibus arcu ligula a, id hendrerit dolor nec nec
        placerat. Vel ornare tincidunt tincidunt, erat amet mollis quisque, odio
        cursus gravida libero aliquam duis, dolor sed nulla dignissim praesent
        erat, voluptatem pede aliquam. Ut et tellus mi fermentum varius, feugiat
        nullam nunc ultrices, ullamcorper pede, nunc vestibulum, scelerisque
        nunc lectus integer. Nec id scelerisque vestibulum, elit sit, cursus
        neque varius. Fusce in, nunc donec, volutpat mauris wisi sem, non
        sapien. Pellentesque nisl, lectus eros hendrerit dui. In metus aptent
        consectetuer, sociosqu massa mus fermentum mauris dis, donec erat nunc
        orci.
      </div>
    </div>
  </div>
</template>

<script>
export default {
  data() {
    return {
      contentWidth: 1000
    }
  },
  methods: {
    handleScroll(event) {
      if (event.target._prevClass === 'content') {
        this.$refs.topScroll.scrollLeft = this.$refs.content.scrollLeft
      } else {
        this.$refs.content.scrollLeft = this.$refs.topScroll.scrollLeft
      }
    }
  }
}
</script>

<style lang="scss" scoped>
.top-scroll,
.content {
  overflow: auto;
  max-width: 100%;
}
.top-scroll {
  margin-top: 50px;
}
</style>

TAKE NOTE on the height: 12px.. I made it 12px so it will be noticed on Mac Users as well. But w/ windows, 0.1px is good enough

Abracadabra answered 30/5, 2020 at 3:28 Comment(0)
C
2

Angular version

I combined 2 answers here. (@simo and @bresleveloper)

https://stackblitz.com/edit/angular-double-scroll?file=src%2Fapp%2Fapp.component.html

inline-component

@Component({
  selector: 'app-double-scroll',
  changeDetection: ChangeDetectionStrategy.OnPush,
  template: `
    <div class="wrapper1" #wrapper1>
      <div class="div1" #div1></div>
    </div>
    <div class="wrapper2" #wrapper2>
        <div class="div2" #div2>
            <ng-content></ng-content>
        </div>
    </div>
  `,
  styles: [
    `
      .wrapper1, .wrapper2 { width: 100%; overflow-x: auto; overflow-y: hidden; }
    `,
    `
      .div1 { overflow: hidden; height: 0.5px;}
    `,
    `
      .div2 { overflow: hidden; min-width: min-content}
    `
  ]
})
export class DoubleScrollComponent implements AfterViewInit {

  @ViewChild('wrapper1') wrapper1: ElementRef<any>;
  @ViewChild('wrapper2') wrapper2: ElementRef<any>;

  @ViewChild('div1') div1: ElementRef<any>;
  @ViewChild('div2') div2: ElementRef<any>;

  constructor(private _r: Renderer2, private _cd: ChangeDetectorRef) {
  }


  ngAfterViewInit() {

    this._cd.detach();

    this._r.setStyle(this.div1.nativeElement, 'width', this.div2.nativeElement.clientWidth + 'px' );

    this.wrapper1.nativeElement.onscroll = e => this.wrapper2.nativeElement.scroll((e.target as HTMLElement).scrollLeft, 0)
    this.wrapper2.nativeElement.onscroll = e => this.wrapper1.nativeElement.scroll((e.target as HTMLElement).scrollLeft, 0)

  }

}

example

<div style="width: 200px; border: 1px black dashed">

  <app-double-scroll>
    <div style="min-width: 400px; background-color: red; word-break: keep-all; white-space: nowrap;">
      long ass text long ass text long ass text long ass text long ass text long ass text long ass text long ass text long ass text 
    </div>
  </app-double-scroll>

  <br>
  <hr>
  <br>

  <app-double-scroll>
    <div style="display: inline-block; background-color: green; word-break: keep-all; white-space: nowrap;">
      short ass text
    </div>
  </app-double-scroll>

  <br>
  <hr>
  <br>

  <app-double-scroll>
    <table width="100%" border="0" cellspacing="0" cellpadding="0">
      <tbody>
        <tr>
          <td>table cell</td>
          <td>table cell</td>
          <td>table cell</td>
          <td>table cell</td>
          <td>table cell</td>
          <td>table cell</td>
          <td>table cell</td>
          <td>table cell</td>
          <td>table cell</td>
          <td>table cell</td>
          <td>table cell</td>
          <td>table cell</td>
          <td>table cell</td>
          <td>table cell</td>
          <td>table cell</td>
          <td>table cell</td>
          <td>table cell</td>
          <td>table cell</td>
          <td>table cell</td>
          <td>table cell</td>
          <td>table cell</td>
          <td>table cell</td>
          <td>table cell</td>
          <td>table cell</td>
          <td>table cell</td>
          <td>table cell</td>
          <td>table cell</td>
          <td>table cell</td>
          <td>table cell</td>
          <td>table cell</td>
          <td>table cell</td>
          <td>table cell</td>
          <td>table cell</td>
          <td>table cell</td>
          <td>table cell</td>
          <td>table cell</td>
          <td>table cell</td>
          <td>table cell</td>
          <td>table cell</td>
          <td>table cell</td>
          <td>table cell</td>
          <td>table cell</td>
          <td>table cell</td>
          <td>table cell</td>
          <td>table cell</td>
          <td>table cell</td>
          <td>table cell</td>
          <td>table cell</td>
          <td>table cell</td>
          <td>table cell</td>
          <td>table cell</td>
          <td>table cell</td>
          <td>table cell</td>
          <td>table cell</td>
          <td>table cell</td>
          <td>table cell</td>
          <td>table cell</td>
          <td>table cell</td>
          <td>table cell</td>
          <td>table cell</td>
          <td>table cell</td>
          <td>table cell</td>
          <td>table cell</td>
          <td>table cell</td>
          <td>table cell</td>
          <td>table cell</td>
          <td>table cell</td>
          <td>table cell</td>
        </tr>
      </tbody>
    </table>
  </app-double-scroll>

</div>
Chalmers answered 1/10, 2020 at 10:31 Comment(0)
P
1

to all angular/nativeJs fans, implementing @simo's answer

HTML (no change)

<div class="top-scroll-wrapper">
    <div class="top-scroll"></div>
</div>

CSS (no change, width: 90% is my desing)

.top-scroll-wrapper { width: 90%;height: 20px;margin: auto;padding: 0 16px;overflow-x: auto;overflow-y: hidden;}
.top-scroll { height: 20px; }

JS (like onload) or ngAfterViewChecked (all the as are for TypeScript)

let $topscroll = document.querySelector(".top-scroll") as HTMLElement
let $topscrollWrapper = document.querySelector(".top-scroll-wrapper") as HTMLElement
let $table = document.querySelectorAll('mat-card')[3] as HTMLElement

$topscroll.style.width = totalWidth + 'px'
$topscrollWrapper.onscroll = e => $table.scroll((e.target as HTMLElement).scrollLeft, 0)
$table.onscroll = e => $topscrollWrapper.scroll((e.target as HTMLElement).scrollLeft, 0)
Parochial answered 10/7, 2019 at 13:36 Comment(0)
P
1

This is my solution in vanilla js for simple usage with Bootstrap:

<div class="table-responsive table-responsive-scrollbar-top"></div>
<div class="table-responsive">
    <table class="table">
    <!-- ... table code ... -->
    </table>
</div>

<script>
    window.addEventListener('DOMContentLoaded', function() {
        let mains = document.querySelectorAll('.table-responsive');
        if (mains.length > 0) {
            Array.from(mains).forEach(function(main) {
                let top = main.previousElementSibling.matches('.table-responsive-scrollbar-top') ? main.previousElementSibling : null;
                if (top) {
                    let timeout = false;
                    let toggleScrollbar;

                    top.style.display = 'none';
                    
                    if (!top.firstElementChild) {
                        top.appendChild(document.createElement("div"));
                    }

                    (toggleScrollbar = function() {
                        
                        if (main.offsetWidth < main.scrollWidth) {
                            top.style.display = 'block';
                            top.style.height = (main.offsetHeight - main.clientHeight) + 'px';
                            top.firstElementChild.style.width = main.scrollWidth + 'px';
                        } else {
                            top.style.display = 'revert';
                        }
                    })();

                    addEventListener('resize', (event) => {
                        clearTimeout(timeout);
                        timeout = setTimeout(toggleScrollbar, 250);
                    });

                    top.addEventListener('scroll', function(e) {
                        main.scrollLeft = top.scrollLeft;
                    });
                    main.addEventListener('scroll', function(e) {
                        top.scrollLeft = main.scrollLeft;
                    });
                }
            });
        }
    });
</script>

https://github.com/lsblsb/bootstrap-table-responsive-scrollbar-top

Pentheas answered 9/11, 2022 at 10:41 Comment(0)
A
1

I've been trying to find an answer for react version but it was really difficult to find a proper solution. Finally I got a fix from npm.

React-Double Scrollbar

https://www.npmjs.com/package/react-double-scrollbar

render() {
    return (
      <div>
        <DoubleScrollbar>
          <table>...</table>
        </DoubleScrollbar>
      </div>
    );
  }
Axenic answered 9/5, 2023 at 14:51 Comment(1)
I can't import this component. After doing npm install react-double-scrollbar --save and importing it, there's an error Could not find a declaration file for for module react-double-scrollbar`,Inhabited
H
0

If you are using iscroll.js on webkit browser or mobile browser, you could try:

$('#pageWrapper>div:last-child').css('top', "0px");
Homophile answered 1/9, 2013 at 15:39 Comment(0)
B
0

An AngularJs directive for achieving this: To use it, add css class double-hscroll to your element. You will need jQuery and AngularJs for this.

import angular from 'angular';

var app = angular.module('plunker', []);
app.controller('MainCtrl', function($scope, $compile) {
  $scope.name = 'Dual wielded horizontal scroller';
});

app.directive('doubleHscroll', function($compile) {
  return {
restrict: 'C',
link: function(scope, elem, attr){

  var elemWidth = parseInt(elem[0].clientWidth);

  elem.wrap(`<div id='wrapscroll' style='width:${elemWidth}px;overflow:scroll'></div>`); 
  //note the top scroll contains an empty space as a 'trick' 
  $('#wrapscroll').before(`<div id='topscroll' style='height:20px; overflow:scroll;width:${elemWidth}px'><div style='min-width:${elemWidth}px'> </div></div>`);

  $(function(){
    $('#topscroll').scroll(function(){
      $("#wrapscroll").scrollLeft($("#topscroll").scrollLeft());
    });
    $('#wrapscroll').scroll(function() {
      $("#topscroll").scrollLeft($("#wrapscroll").scrollLeft());
    });

  });  

}

  };


});
Bernardabernardi answered 28/5, 2020 at 15:0 Comment(0)
P
0

Extending Dario Digregorio https://mcmap.net/q/125515/-horizontal-scrollbar-on-top-and-bottom-of-table answer (posted here) I was able to move the scrollbar above and below the content when the bottom of the container is scrolled into view or not.

    setTimeout(()=>{
      const container = document.querySelector('.container');
      const content = document.querySelector('.content');
      handleWindowScroll();

      function handleWindowScroll() {
        const viewportHeight = window.innerHeight;
        const containerRect = container.getBoundingClientRect();
        const containerTop = containerRect.top;
        const containerHeight = containerRect.height;
        const containerBottom = containerTop + containerHeight;

        const scrollThreshold = 100; // adjust this value as needed
        if (containerBottom <= viewportHeight + scrollThreshold) {
          // bottom of container is visible in viewport
          container.style.transform = '';
          content.style.transform = '';
        } else {
          // bottom of container is not visible in viewport
          container.style.transform = 'rotateX(180deg)';
          content.style.transform = 'rotateX(180deg)';
        }
      }

      window.addEventListener('scroll', handleWindowScroll);

    }, 200);        

Note that I am using setTimeout because of another component that I have to wait a bit, thus you may not need to use it.

Petroglyph answered 2/4, 2023 at 5:30 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.