繁体   English   中英

表格顶部和底部的水平滚动条

[英]horizontal scrollbar on top and bottom of table

我的页面上有一个非常大的table 所以我决定在表格底部放一个水平滚动条。 但我希望这个滚动条也位于桌面的顶部。

我在模板中的内容是这样的:

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

仅在 HTML 和 CSS 中可以做到这一点吗?

要在元素顶部模拟第二个水平滚动条,请在具有水平滚动的元素上方放置一个“虚拟” div ,其高度足以容纳滚动条。 然后为虚拟元素和真实元素附加“滚动”事件的处理程序,以便在移动任一滚动条时使另一个元素同步。 虚拟元素看起来像真实元素上方的第二个水平滚动条。

一个活生生的例子,看这个小提琴

这是代码

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());
  });
});

尝试使用jquery.doubleScroll插件

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>

没有 jQuery (2017)

因为您可能不需要 JQuery,所以这是一个基于 @StanleyH 答案的有效 Vanilla JS 版本:

 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>

仅使用 CSS 的解决方案

有一种方法可以实现这一点,我在这里没有看到任何人提到。

通过将父容器旋转180 度并将子容器再次旋转 180 度,滚动条将显示在顶部

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

有关参考,请参阅w3c 存储库中的问题。

首先,很好的答案,@StanleyH。 如果有人想知道如何制作具有动态宽度的双滚动容器:

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>

演示

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

StanleyH 的回答非常好,但它有一个不幸的错误:单击滚动条的阴影区域不再跳转到您单击的选择。 相反,您得到的是滚动条位置的一个非常小且有点烦人的增量。

测试:4 个版本的 Firefox(100% 受影响),4 个版本的 Chrome(50% 受影响)。

这是我的jsfiddle 您可以通过使用 on/off (true/false) var 来解决此问题,该 var 一次只允许触发一个 onScroll() 事件:

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());
});

接受答案的问题行为:

实际期望的行为:

那么,为什么会发生这种情况? 如果你运行代码,你会看到 wrapper1 调用了 wrapper2 的 scrollLeft,而 wrapper2 调用了 wrapper1 的 scrollLeft,并且无限循环,所以,我们有一个无限循环的问题。 或者,更确切地说:用户的继续滚动与 wrapperx 的滚动调用冲突,发生事件冲突,最终结果是滚动条没有跳转。

希望这对其他人有所帮助!

您可以使用一个 jQuery 插件来为您完成这项工作:

该插件将为您处理所有逻辑。

基于@HoldOffHunger 和@bobince 答案的纯javascript 解决方案

<div id="doublescroll">
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"));

基于@StanleyH 解决方案,我在jsFiddle上创建了一个AngularJS 指令演示。

便于使用:

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

对于 AngularJS 开发人员

据我所知,这对于 HTML 和 CSS 是不可能的。

链接滚动条是有效的,但它的编写方式会创建一个循环,如果您单击较轻滚动条的部分并按住它(而不是在拖动滚动条时),则在大多数浏览器中滚动会变慢。

我用一个标志修复了它:

$(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;
        }
    });
});

扩展 StanleyH 的答案,并尝试找到所需的最低要求,这是我实施的:

JavaScript(从$(document).ready()类的地方调用一次):

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

HTML(注意宽度会改变滚动条的长度):

<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>

而已。

在 vanilla Javascript/Angular 中,您可以这样做:

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;
}

这是VueJS的示例

索引页

<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>

注意height: 12px .. 我把它设置为 12px,这样 Mac 用户也会注意到它。 但是带窗户,0.1px 就足够了

角版

我在这里合并了2个答案。 (@simo 和 @bresleveloper)

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

内联组件

@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)

  }

}

例子

<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>

React+TypeScript rap-2-h 的代码移植到 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>
  );
}

致所有 angular/nativeJs 粉丝,实施@simo 的回答

HTML(无变化)

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

CSS(没有变化,宽度:90% 是我的设计)

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

JS(如onload )或ngAfterViewChecked (所有as都是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)

如果您在 webkit 浏览器或移动浏览器上使用 iscroll.js,您可以尝试:

$('#pageWrapper>div:last-child').css('top', "0px");

实现此目的的 AngularJs 指令:要使用它,请将 css 类 double-hscroll 添加到您的元素。 为此,您将需要 jQuery 和 AngularJs。

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());
    });

  });  

}

  };


});

滚动方向存在问题:rtl。 该插件似乎没有正确支持它。 这是小提琴:小提琴

请注意,它在 Chrome 中可以正常工作,但在所有其他流行的浏览器中,顶部滚动条的方向仍然是左侧。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM