Горизонтальная полоса прокрутки сверху и снизу таблицы
У меня очень большой table
на моей странице. Поэтому я решил поставить горизонтальную полосу прокрутки, которая находится в нижней части таблицы. Но я бы хотел, чтобы эта полоса прокрутки была также сверху на столе.
Что я имею в шаблоне это:
<div style="overflow:auto; width:100%; height:130%">
<table id="data" style="width:100%">
Это возможно сделать только в HTML и CSS?
23 ответа
Чтобы смоделировать вторую горизонтальную полосу прокрутки поверх элемента, поместите "пустышку" div
над элементом, имеющим горизонтальную прокрутку, достаточно высоко для полосы прокрутки. Затем присоедините обработчики события "scroll" для фиктивного элемента и реального элемента, чтобы синхронизировать другой элемент при перемещении полосы прокрутки. Фиктивный элемент будет выглядеть как вторая горизонтальная полоса прокрутки над реальным элементом.
Для живого примера, см. Эту скрипку
Вот код:
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());
});
});
Есть один способ добиться этого, о котором я не видел, чтобы здесь кто-то упоминал.
При повороте родительского контейнера на 180 градусов и дочернего контейнера снова на 180 градусов полоса прокрутки будет показана вверху.
.parent {
transform: rotateX(180deg);
overflow-x: auto;
}
.child {
transform: rotateX(180deg);
}
Для справки см. Проблему в репозитории w3c.
Попробуйте использовать 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, вот рабочая версия Vanilla JS, основанная на ответе @StanleyH:
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>
Ответ StanleyH был превосходным, но у него была одна неприятная ошибка: нажатие на затененную область полосы прокрутки больше не переходит к выбранному вами выбору. Вместо этого вы получите очень маленький и несколько раздражающий прирост положения полосы прокрутки.
Протестировано: 4 версии Firefox (затронуты на 100%), 4 версии Chrome (затронуты на 50%).
Вот мой jsfiddle. Вы можете обойти это с таймаутом:
var timecount = 25;
var timer = null;
$(".wrapper1").scroll(function(){
if(timer !== null) {
clearTimeout(timer);
}
timer = setTimeout(function() {
$(".wrapper2").scrollLeft($(".wrapper1").scrollLeft());
}, timecount);
});
Поведение проблемы с принятым ответом:
Фактически желаемое поведение:
Так почему же это происходит? Если вы выполните код, вы увидите, что wrapper1 вызывает scrollLeft для wrapper2, а wrapper2 вызывает scrollLeft для wrapper1 и повторяет это бесконечно, поэтому у нас возникает проблема с бесконечным циклом. Или, скорее: продолжающаяся прокрутка пользователя вступает в конфликт с вызовом прокрутки от Wrappperx, и конечным результатом является отсутствие перехода по полосам прокрутки.
Надеюсь, это поможет кому-то еще!
Прежде всего, отличный ответ @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>
демонстрация
Решение только для javascript, основанное на ответах @HoldOffHunger и @bobince
<div id="doublescroll">
.
function DoubleScroll(element) {
var 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'));
var running = false;
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('doublescroll'));
Вы можете использовать плагин jquery, который сделает всю работу за вас:
Плагин будет обрабатывать всю логику для вас...
Код React+TypeScriptrap-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>
);
}
На основе решения @StanleyH я создал директиву AngularJS, демо-версию для jsFiddle.
Легко использовать:
<div data-double-scroll-bar-horizontal> {{content}} or static content </div>
Не требуется jQuery.
Соединение скроллеров работало, но в том виде, в котором оно написано, оно создает цикл, который замедляет прокрутку в большинстве браузеров, если щелкнуть часть более легкой полосы прокрутки и удерживать ее (а не при перетаскивании скроллера).
Я исправил это с флагом:
$(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;
}
});
});
Вот пример для 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>
ОБРАТИТЕ ВНИМАНИЕ наheight: 12px
.. Я сделал его 12px, чтобы его заметили и пользователи Mac. Но с окнами достаточно 0,1 пикселя
Угловая версия
Я объединил здесь 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>
Расширяя ответ Стэнли и пытаясь найти необходимый минимум, вот что я реализовал:
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>
Вот и все.
В ванильном 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;
}
Это мое решение в vanilla js для простого использования с 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
Всем поклонникам 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)
Если вы используете iscroll.js в веб-браузере или мобильном браузере, вы можете попробовать:
$('#pageWrapper>div:last-child').css('top', "0px");
Расширение ответа Дарио Дигрегорио /questions/46942503/gorizontalnaya-polosa-prokrutki-sverhu-i-snizu-tablitsyi/55161106#55161106 (опубликовано здесь). Мне удалось переместить полосу прокрутки выше и ниже содержимого, когда нижняя часть контейнера прокручивается в поле зрения или нет.
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);
Обратите внимание, что я использую setTimeout из-за другого компонента, который мне придется немного подождать, поэтому вам, возможно, не понадобится его использовать.
Директива AngularJs для достижения этой цели: чтобы использовать ее, добавьте в свой элемент двойной hscroll класса css. Для этого вам понадобятся 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());
});
});
}
};
});
Я пытался найти ответ наreact
версию, но найти подходящее решение было действительно сложно. Наконец я получил исправление от npm .
React-двойная полоса прокрутки
https://www.npmjs.com/package/react-double-scrollbar
render() {
return (
<div>
<DoubleScrollbar>
<table>...</table>
</DoubleScrollbar>
</div>
);
}
Существует проблема с направлением прокрутки: RTL. Кажется, плагин не поддерживает его правильно. Вот скрипка: jsfiddle.net/qsn7tupc/3
Обратите внимание, что в Chrome он работает правильно, но во всех других популярных браузерах направление верхней полосы прокрутки остается левым.