Решая на чистом js задачу скроллинга клавишами, а также липких блоков из
Перемещение блока при скроллинге столкнулся с рядом проблем.
Основа: набор таблиц, в каждой из которой набор строк, в первой ячейке каждой строки произвольное содержимое, вторая ячейка - контейнер для липкого блока.
Липкие блоки: (см ссылку).
Скроллинг: по нажатию символьных клавиш, которые символизируют "вверх" и "вниз" последовательно перемещаться по ячейкам всей совокупности таблиц.
Взял за основу метод document.elementFromPoint(x, y), с помощью которого получаю блок и далее работаю с ним (либо перемещаю к нужной ячейке при скроллинге, либо контролирую положение соответствующего липкого блока).
По скроллингу: перемещения производятся, но в некоторые моменты document.elementFromPoint(x, y) возвращает не нужный блок, а саму таблицу (даже если не учитывать, что специально прокрутили, чтобы попасть в margin между ячейками, тоже пока не соображу как это обойти), хотя вроде бы чётко смещаю при каждом нажатии клавиши левый верхний угол блока в точку, в которой находился первый блок при загрузке страницы.
По липким блокам: задача вроде бы тоже не сложная, но не получается cформировать условия (совладать с координатами, рисование пока не помогает).
Может быть у кого есть соображения на эту тему по моей логике решения (необходимый, на мой взгляд, функционал я включил в код), или может быть альтернативные подходы к решению этих задач.
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>Скроллинг клавишами и липкие блоки</title>
</head>
<style>
.menu {
position: fixed;
left: 0%;
top: 0%;
background: maroon;
width: 100%;
height: 10%;
z-index: 10;
}
#content {
position: absolute;
left: 0%;
top: 10%;
}
.table {
position: relative;
background: gray;
}
.left {
width: 500px;
background: lightgray;
border: 1px solid black;
}
.right {
background: gray;
border: 1px solid black;
width: 200px;
vertical-align: top;
}
.sticker {
position: relative;
background: whitesmoke;
height: 100px;
}
</style>
<div class="menu"></div>
<div id="content"></div>
<script>
window.onload = function () {//onload begin
var content = document.getElementById('content');
//код создания таблиц
var inner = '';
var str = '';
for (var i = 0; i < 3; i++) {
str += '<table class="table">'
for (var j = 0; j < 3; j++) {
str += '<tr><td class="left">';
for (var k = 0; k < 31; k++) {
inner += 'таблица ' + i + '; строка ' + j + '<br>';
}
str += inner + '</td><td class="right"><div class="sticker">sticker' + i + ' - ' + j + '</div></td></tr>';
inner = '';
}
str += '</table>'
}
content.innerHTML = str;
//функция получения координат элемента (и не только) относительно документа
var getCoords = function (element) {//getCoords begin
var elementRect = element.getBoundingClientRect();
var scrollLeft = window.pageXOffset || document.documentElement.scrollLeft || document.body.scrollLeft;
var scrollTop = window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop;
var clientLeft = document.documentElement.clientLeft || document.body.clientLeft || 0;
var clientTop = document.documentElement.clientTop || document.body.clientTop || 0;
var left = elementRect.left + scrollLeft - clientLeft;
var top = elementRect.top + scrollTop - clientTop;
var obj = {
left: Math.round(left), top: Math.round(top),
scrollTop: Math.round(scrollTop), scrollLeft: Math.round(scrollLeft),
clientLeft: Math.round(clientLeft), clientTop: Math.round(clientTop)
}
return obj;
}//getCoords end
//функция получения номера по элементу, если элемент не в коллекции -1
var getN = function (element, collection) {//getN begin
for (var i = 0; i < length; i++) {
if (element == collection[i]) {
return i
}
}
return -1;
}//getN end
//функция получения элемента по номеру, если нет такого элемента -1
var getElement = function (n, collection) {//getElement begin
for (var i = 0; i < length; i++) {
if (collection[i] == collection[n]) {
return collection[i];
}
}
return -1;
}//getElement end
//необходимые переменные
var leftBlocks = content.getElementsByClassName('left'); //колллекция блоков слева
var rightBlocks = content.getElementsByClassName('right');//коллекция блоков справа
var stickers = content.getElementsByClassName('sticker');//коллекция липких блоков
var length = leftBlocks.length; // = rightBlocks.length; //= stickers.length;
var firstLeft = leftBlocks[0]; //первый элемент слева
//его координаты - точка отсчёта
var upperBound = getCoords(leftBlocks[0]).top;//верхняя граница
var leftBound = getCoords(leftBlocks[0]).left; //левая граница
var lastLeft = leftBlocks[length - 1]; //последний элемент слева
var lastLeftCoords = getCoords(lastLeft); //его координаты
//скроллинг клавишами++++++++++++++++++++++++++++++++++++++++++++++++++++++
document.onkeyup = function (e) {//onkeyup begin
var e = e || window.event; //для IE
//получаем элемент на верхней границе и его параметры
var element = document.elementFromPoint(leftBound, upperBound);
/****************************************************
вот в этих двух функциях getToUp и getToDown не получается заложить
необходиму логику решения***********************************/
var getToUp = function (element) {//getToUp begin
var n = getN(element, leftBlocks);
var coords = getCoords(element);
if ( n != -1 ) {//если элемент действительно тот //if begin
//если элемент пересекает верхнюю границу
if ( coords.top < coords.scrollTop + upperBound ) {
return element; //возвращаем сам элемент
} else if ( n != 0 ) {
return element = getElement(n - 1, leftBlocks);//возвращаем предыдущий элемент, если он не первый
}
} else {//если элемент не тот //if else
if ( coords.scrollTop > upperBound) {
return element = firstLeft;//возвращаем первый элемент
} else {
return element = lastLeft;//возвращаем последний элемент
}
}//if end
}//getToUp end
var getToDown = function (element) {//getToDown begin
var n = getN(element, leftBlocks);
var coords = getCoords(element);
if ( n != -1 ) { //if begin
if ( coords.top < coords.scrollTop + upperBound && n != length ) {
return element; //возвращаем последующий элемент, если он не последний
} else {
return element;//возвращаем сам элемент
}
} else {//if else
if ( coords.scrollTop > upperBound) {
//return element = ;
} else {
//return element = ;
}
}//if end
}//getToDown end
//проверяем клавиши и получаем нужный блок
switch ( e.keyCode ) {
case 65: {//нажата клавиша "вверх" (Ф, ф - рус., A, a - eng.)
getToUp (element);
break;
}
case 83: {//нажата клавиша "вниз" (В, в - рус., S, s - eng.)
getToDown(element);
break;
}
default: return; //вышли, если не та клавиша
}//switch end
//если дошли, то прокрутили страницу, поместив блок в точку остчёта
window.scrollTo(getCoords(element).left - leftBound, getCoords(element).top - upperBound);
}//onkeyup end
//липкие блоки+++++++++++++++++++++++++++++++++++++++++
window.onscroll = function () {//onscroll begin
//получаем элемент
var element = document.elementFromPoint(leftBound, upperBound);
//var rect = element.getBoundingClientRect();
var n = getN(element, leftBlocks);
var elementCoords = getCoords(element);//координаты элемента относительно документа
var elementHeight = element.clientHeight;//высота элемента
var sticker = getElement(n, stickers);//текущий липкий блок
var stickerCoords = getCoords(sticker);//координаты липкого блока относительно документа
var stickerHeight = sticker.clientHeight;//высота липкого блока
var scroll = elementCoords.scrollTop;//= stickerCoords.scrollTop //величина прокрутки
var scrollAndUpperBound = scroll + upperBound; //величина прокуртки с учётом верхней границы
var displacement = scrollAndUpperBound - elementCoords.top; //величина смещения
//выравнивание по верхней границе
if ( stickerCoords.top < scrollAndUpperBound || stickerCoords.top > scrollAndUpperBound) {
sticker.style.top = displacement + 'px';
}
//получение новых координат
stickerCoords = getCoords(sticker);
scrollAndUpperBound = stickerCoords.scrollTop + upperBound;
elementCoords = getCoords(element);
displacement = scrollAndUpperBound - elementCoords.top;
//выравнивание по нижней границе
if ( stickerCoords.top + stickerHeight >= elementCoords.top + elementHeight ) {
sticker.style.top = elementHeight - stickerHeight + 'px';
} else if (stickerCoords.top > scrollAndUpperBound ) {
sticker.style.top = displacement + 'px';
}
if (element != lastLeft) {
var nextSticker = getElement(n + 1, stickers);
nextSticker.style.top = '0 px';
}
if ( element == firstLeft && displacement == 0 ) {
sticker.style.top = '0 px';
}
}//onscroll end
}//onload end
</script>
</body>
</html>