Javascript-форум (https://javascript.ru/forum/)
-   Общие вопросы Javascript (https://javascript.ru/forum/misc/)
-   -   Скроллинг клавишами и липкие блоки (https://javascript.ru/forum/misc/28985-skrolling-klavishami-i-lipkie-bloki.html)

bes 09.06.2012 19:39

Скроллинг клавишами и липкие блоки
 
Решая на чистом js задачу скроллинга клавишами, а также липких блоков из http://javascript.ru/forum/misc/2892...krollinge.html столкнулся с рядом проблем.
Основа: набор таблиц, в каждой из которой набор строк, в первой ячейке каждой строки произвольное содержимое, вторая ячейка - контейнер для липкого блока.
Липкие блоки: (см ссылку).
Скроллинг: по нажатию символьных клавиш, которые символизируют "вверх" и "вниз" последовательно перемещаться по ячейкам всей совокупности таблиц.

Взял за основу метод 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>

nerv_ 09.06.2012 23:58

Цитата:

Сообщение от bes
вторая ячейка - контейнер для липкого блока.

зачем? div с абсолютным позиционированием, не?

Что надо в целом вообще не понял. Правильно сформулированная задача уже часть решения

bes 10.06.2012 00:29

nerv_, посмотрите подредактированный пример, в принципе, хотя пока и коряво, удалось частично решить вторую часть задачи - блоки начали двигаться примерно так как надо, то есть липнуть при прокрутке к нижней части строки меню (тут можно и с position: relative, главное правильно рассчитать координаты, что собственно и не получается).
Насчёт скроллинга клавишами - когда нажимаете клавишу "вверх" должно прокручиваться на предыдущую строку от той, которая в данный момент сразу под строкой меню, "вниз" - на следующую строку, для прокрутки здесь используются клавиши A - вверх, S - вниз.

bes 10.06.2012 12:26

Липкие блоки более-менее работают (в FF наиболее плавно, в хроме и IE видны подёргивания при прокрутке), но остаётся проблема попадания на нужный блок при помощи document.elementFromPoint(x, y).
Кто-нибудь знает как отследить ближайший блок, если этот метод выдал в качестве элемента, например, не ячейку таблицы, а саму таблицу (например, когда попал в margin между ячейками)?

Специально стал использовал document.elementFromPoint(x, y), чтобы обойтись без цикла, но при таком раскладе проще будет всё рассчитать, если за основу поиска взять именно цикл, а не этот метод. Может быть можно как-нибудь обойтись без цикла?

nerv_ 10.06.2012 13:00

bes, добрый день :)

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

obj - глобальная переменная
getN = function (element, collection) {//getN begin

getN - глобальная
getElement = function (n, collection) {//getElement begin

getElement - глобальная
getCoords = function (element) {//getCoords begin

getCoords - глобальная

Дальше продолжать? )

Цитата:

Сообщение от bes
главное правильно рассчитать координаты, что собственно и не получается

Кстати в IE8 не работает. При загрузки страницы один раз вычисляешь положение каждого блока (при ресайзе перевычисляешь), при скроллине смотришь, какой блок ближе (первое, что приходит в голову)

bes 10.06.2012 13:30

nerv_, спасибо, подправил, попробую и этот способ.
В IE8 не работает из-за getElementsByClassName().

Deff 10.06.2012 14:14

bes,
:yes: Мож стоит типо:
1. Выяснем, какие блоки находяться в зоне видимости,
2. Тестируем расстояние нижнего края верхнего видимого блока(посколь их может быть и два) от
верха экрана, ежели оно менее его высоты, даем липкому блоку спецфическое id и позицию fixed , ежели оно менее высоты липкого блока, вычислем его координаты и закрепляем позицией absolute убираем id
id - удобно для ie

bes 11.06.2012 13:15

Deff, я взял за основу несколько другой способ, его уж и закончу))

<!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 bottom = elementRect.bottom + scrollTop - clientTop;

    var obj = {
       left: Math.round(left), 
       top: Math.round(top), 
       bottom: Math.round(bottom),
    }
    return obj;
  }//getCoords end


 //функция получения координат всех элементов 
  var getAllCoords = function (collection) {//getAllCoords begin
    var allCoords = [];
    for (var i = 0; i < length; i++) {
      allCoords[i] = getCoords(collection[i]);
    }
    return allCoords;
  }//getAllCoords end


  if (!content.getElementsByClassName) { //if begin
    content.getElementsByClassName = function(cl) {//getElementsByClassName begin
      var retnode = []; 
      var myclass = new RegExp('\\b'+cl+'\\b'); 
      var elem = this.getElementsByTagName('*'); 
      for (var i = 0; i < elem.length; i++) { 
         var classes = elem[i].className; 
         if (myclass.test(classes)) { 
            retnode.push(elem[i]); 
         } 
      } 
      return retnode; 
   }//if end
}//getElementsByClassName 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 allCoords = getAllCoords(leftBlocks);
  var upperBound = allCoords[0].top;//верхняя граница

  //пересчитываем координаты при масштабировании страницы
  window.onresize = function () {
    allCoords = getAllCoords(leftBlocks);
    upperBound = allCoords[0].top;
  }



//скроллинг клавишами++++++++++++++++++++++++++++++++++++++++++++++++++++++

  document.onkeyup = function (e) {//onkeyup begin
    var e = e || window.event; //для IE

    //получаем прокрутку
    var scrollTop = window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop;

    //функция получения номера элемента на верхней границе для движения вверх
    var getNUp = function(coords, boundary) {//getNUp begin
      //расстояние от начала документа до верхней границы
      var sum = scrollTop + boundary;

      for (var i = 0; i < length; i++) {//for begin
        //если элемент ниже верхней границы и не первый, возвращаем номер предыдущего элемента
        if (coords[i].top >= sum && i != 0) {
          return (i - 1);
        } else
        //иначе, если элемент пересекает верхнюю границу, возвращаем его номер
        if (coords[i].bottom > sum) {
          return i;
        }  
      }//for end

      //если дошли, то возвращаем номер самого элемента (он будет последним при полной прокрутке)
      return (i - 1);

  }//getNUp end


    //функция получения номера элемента на верхней границе для движения вниз
    var getNDown = function (coords, boundary) {//getNDown begin
      //расстояние от начала документа до верхней границы
      var sum = scrollTop + boundary; 

      for (var i = 0; i < length; i++) {//for begin
        //если элемент пересекает верхнюю границу, возвращаем номер последующего элемента, если он не последний
        if (coords[i].bottom > sum && i != length - 1) {
          return (i + 1);
        } else //иначе, если элемент ниже верхней границы, возвращаем его номер
        if (coords[i].top > sum) {
          return i; //(i - 1), так как i в цикле увеличивается при выходе
        } 
    }//for end

    //если дошли, то возвращаем номер самого элемента (он будет последним при полной прокрутке)
    return (i - 1);//

  }//getNDown end



    //проверяем клавиши и получаем номер нужного блока
    var n = 0; 
    switch ( e.keyCode ) {
      case 65: {//нажата клавиша "вверх" (Ф, ф - рус., A, a - eng.)
        var n = getNUp(allCoords, upperBound);
        break;
      }
      case 83: {//нажата клавиша "вниз" (В, в - рус., S, s - eng.)
        var n = getNDown(allCoords, upperBound);
       break;   
      }
      default: return; //вышли, если не та клавиша
    }//switch end

    //если дошли, то прокрутили страницу, поместив начало блока на верхнюю границу
    window.scrollTo(0, allCoords[n].top - upperBound); 

  }//onkeyup end




//липкие блоки+++++++++++++++++++++++++++++++++++++++++++++++++++++++++

   window.onscroll = function () {//onscroll begin
    var scrollTop = window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop;
    var sum = scrollTop + upperBound;
    var stickerCoords = {};

    //получаем липкий блок и задаём ему нужные координаты относительно родительского контейнера
    for (var i = 0; i < length; i++) {//for begin
      stickerCoords = getCoords(stickers[i]);

     //если липкий блок в границах своего контейнера, то выровнять липкий блок по верхней границе
      if (stickerCoords.top >= allCoords[i].top && stickerCoords.bottom <= allCoords[i].bottom ) {
        stickers[i].style.top = sum - allCoords[i].top + 'px';
        //return;
      }

  //корректирование предыдущего действия
      stickerCoords = getCoords(stickers[i]);

     //если липкий блок зашёл за верхнюю границу своего контейнера, то выровнять по верхнему краю своего контейнера
     if (stickerCoords.top < allCoords[i].top) {
        stickers[i].style.top = 0 + 'px';
      }

      //если контейнер липкого блока ниже верхней границы, то выровнять его липкий блок по его верхнему краю
      if (allCoords[i].top > sum) {
        stickers[i].style.top = 0 + 'px';
        return;
      } 
/*
    if (window.getComputedStyle) {
      var stickerMarginBottom = parseInt(getComputedStyle(stickers[i], null).marginBottom);
    } else {
      var stickerMarginBottom = stickers[i].currentStyle.marginBottom;
    }
*/

     //если липкий блок зашёл за нижнюю границу своего контейнера, то выровнять по нижнему краю своего контейнера
     if (stickerCoords.bottom > allCoords[i].bottom) {
       stickers[i].style.top = (allCoords[i].bottom - allCoords[i].top) - (stickerCoords.bottom - stickerCoords.top) - 2 + 'px';
     }

    }//for end
  }//onscroll end


}//onload end


</script>
</body>
</html>

bes 11.06.2012 20:50

Подредактировал предыдущий пример, теперь рабочий (в IE<9 не будет работать из-за getElementsByClassName и getComputedStyle, тут нужно эмулировать, остальное вроде бы кроссбраузерно).
Лучше сформулировать логику установки координат для липких блоков пока не могу, тут если умные люди подключатся, подскажут, но, главное, работает.
nerv_, Deff, спасибо за помощь и участие.

bes 12.06.2012 20:45

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

  .block {
    position: relative;
    width: 500px;
    background: lightgray;
    border: 1px solid black;
  }

 .stickerBlock {
    background: gray;
    border: 1px solid black;
    width: 200px;
    vertical-align: top;
  }

  .sticker {
    position: relative;
    background: whitesmoke; 
    height: 100px;
    width: 200px;
  }

</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="block">';
      for (var k = 0; k < 31; k++) {
        inner += 'таблица ' + i + '; строка ' + j + '<br>'; 
      }
      str += inner + '</td><td class="stickerBlock"><div class="sticker">sticker ' + i + ' - ' + j + '</div></td></tr>';
      inner = '';
    }
    str += '</table>';
  }
  content.innerHTML = str;


  var getCoords = function (element) {//getCoords begin
    var rect = element.getBoundingClientRect();
    var docEl = document.documentElement;
    var body = document.body;
    var scrollLeft = window.pageXOffset || docEl.scrollLeft || body.scrollLeft;
    var scrollTop = window.pageYOffset || docEl.scrollTop || body.scrollTop;
    var clientLeft = docEl.clientLeft || body.clientLeft || 0;
    var clientTop = docEl.clientTop || body.clientTop || 0;
    var left = rect.left + scrollLeft - clientLeft;
    var top  = rect.top +  scrollTop - clientTop;
    var right = rect.right + scrollLeft - clientLeft;
    var bottom = rect.bottom + scrollTop - clientTop;
    return {left: left, top: top, bottom: bottom, right: right}
  }//getCoords end


  if (!content.getElementsByClassName) { //if begin
    content.getElementsByClassName = function(cl) {//getElementsByClassName begin
      var retnode = []; 
      var myclass = new RegExp('\\b'+cl+'\\b'); 
      var elem = this.getElementsByTagName('*'); 
      for (var i = 0; i < elem.length; i++) { 
         var classes = elem[i].className; 
         if (myclass.test(classes)) { 
            retnode.push(elem[i]); 
         } 
      } 
      return retnode; 
   }//getElementsByClassName end
}//if end


/*
 if (!content.getElementsByClassName) { //if begin

    content.getElementsByClassName = function(nameOfClass) {//getElementsByClassName begin
      var mas = []; 
      var elements = this.getElementsByTagName('*'); 
      var length = elements.length;

      for (var i = 0; i < length; i++) { 
        if (elements[i].className == nameOfClass) { 
            mas.push(elements[i]); 
         } 
      } 

      return mas; 
    }//getElementsByClassName end

  }//if end
*/


//скроллинг клавишами++++++++++++++++++++++++++++++++++++++++++++++++++++++

  document.onkeyup = function (e) {//onkeyup begin
    var e = e || window.event; //для IE

    var getNUp = function(collection, boundary) {//getNUp begin
      var scroll = window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop;
      var sum = scroll + boundary;
      var length = collection.length;
      var coords = {};

      for (var i = 0; i < length; i++) {//for begin
        coords = getCoords(collection[i]);
        if (coords.top >= sum && i != 0) {
          return (i - 1);
        } else if (coords.bottom > sum) {
          return i;
        }  
      }//for end

      return (i - 1);
  }//getNUp end


    var getNDown = function (collection, boundary) {//getNDown begin
      var scroll = window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop;
      var sum = scroll + boundary;
      var length = collection.length; 
      var coords = {};

      for (var i = 0; i < length; i++) {//for begin
        coords = getCoords(collection[i]);
        if (i == 0 && coords.top > sum + 1) {return i}
        if (coords.bottom > sum && i != length - 1) {
          return (i + 1);
        } else if (coords.top > sum) {
          return i; 
        } 
    }//for end

    return (i - 1);
  }//getNDown end


  var blocks = content.getElementsByClassName('block');
  var upperBound = getCoords(blocks[0]).top;
  var leftBound = getCoords(blocks[0]).left;
  var n = 0; 

    switch ( e.keyCode ) {//switch begin
      case 65: {
        var n = getNUp(blocks, upperBound);
        break;
      }
      case 83: {
        var n = getNDown(blocks, upperBound);
       break;   
      }
      default: return; 
    }//switch begin

    window.scrollTo(getCoords(blocks[n]).left - leftBound, getCoords(blocks[n]).top - upperBound); 

  }//onkeyup end



//липкие блоки+++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 
   window.onscroll = function () {//onscroll begin
     var stickerBlocks = content.getElementsByClassName('stickerBlock');
     var stickers = content.getElementsByClassName('sticker');
     var length = stickers.length;
     var upperBound = getCoords(stickerBlocks[0]).top;
     var leftBound = getCoords(stickerBlocks[0]).left;
     var scroll = window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop;
     var sum = scroll + upperBound;
     var coords = {};
     var stickerCoords = {};
     var condition;

    for (var i = 0; i < length; i++) {//for begin
      coords = getCoords(stickerBlocks[i]);
      stickerCoords = getCoords(stickers[i]);
      condition = parseInt(stickers[i].style.top) != parseInt(coords.bottom - coords.top - (stickerCoords.bottom - stickerCoords.top) - 1);

    if (coords.bottom < sum) {
       continue;    
    }

    if (coords.top < sum && stickers[i].style.position != 'fixed' && condition) {
      stickers[i].style.position = 'fixed';  
      stickers[i].style.top = upperBound + 'px';
    } 

     coords = getCoords(stickerBlocks[i]);
     stickerCoords = getCoords(stickers[i]);

    if (stickerCoords.bottom > coords.bottom) {
       stickers[i].style.position = 'relative'; 
       stickers[i].style.top = parseInt(coords.bottom - coords.top - (stickerCoords.bottom - stickerCoords.top) - 1) + 'px';
     } 

     coords = getCoords(stickerBlocks[i]);
     stickerCoords = getCoords(stickers[i]);
   
     if (stickerCoords.top < coords.top && i != 0) {
       stickers[i].style.position = 'relative';  
       stickers[i].style.top = 0 + 'px';
     }    
  
     condition = parseInt(stickers[i].style.top) == parseInt(coords.bottom - coords.top - (stickerCoords.bottom - stickerCoords.top) - 1);
     coords = getCoords(stickerBlocks[i]);
     stickerCoords = getCoords(stickers[i]);

     if (stickerCoords.top > sum && condition) {
       stickers[i].style.position = 'fixed';  
       stickers[i].style.top = upperBound + 'px';
       break;
     }

    }//for end

  }//onscroll end

}//onload end

</script>
</body>
</html>

Deff 12.06.2012 21:36

bes,
можно завать высоту окна просмотра
[HTML run height=600]

bes 12.06.2012 22:08

Цитата:

Сообщение от Deff
bes,
можно завать высоту окна просмотра

Подправил, Deff.

Почему-то здесь на сайте при движении вниз останавливается на "Таблица 1; строка 2", то есть не докручивает до третьей таблицы, у себя нормально, даже во фрейме.

И возник вопрос по прокрутке: если перемещаться при помощи клавиш (A - вверх, S - вниз) на приведённом выше примере, то можно заметить, что блоки под меню устанавливаются не на одной линии, чуть выше, чуть ниже.
Проверил расстояние от каждого элемента до верхней границы при прокрутке при помощи getBoundingClientRect: вещественные, чуть больше, чуть меньше, разница в пределах единицы, прокрутку делаю методом scrollTo, видимо, где-то округляет, убрал округление в функции вычисления координат, вроде стало получше, но точности нет.
Может у кого есть мысли о том, как сгладить этот момент?


Часовой пояс GMT +3, время: 02:11.