Как заморозить первый и последний столбцы HTML-таблицы в прокручиваемом div? - PullRequest
13 голосов
/ 13 апреля 2009

У меня есть таблица, в которой есть строка заголовка, а также столбец заголовка и общий столбец с несколькими столбцами между ними.

Примерно так:

Name    Score 1    Score 2  ...  Total
--------------------------------------
John       5          6            86
Will       3          7            82
Nick       7          1            74

Вся таблица определена внутри прокручиваемого div фиксированной ширины, потому что, вероятно, будет большое количество строк "Score", и у меня есть макет страницы фиксированной ширины.

<div id="tableWrapper" style="overflow-x: auto; width: 500px;">
    <table id="scoreTable">
        ...
    </table>
</div>

Я хотел бы, чтобы первый (Name) и последний (Total) столбцы оставались видимыми во время прокрутки внутренних столбцов.

Может кто-нибудь помочь мне с этим?

Редактировать: Я имею в виду только горизонтальную прокрутку - изменено, чтобы указать это.


Обновление: Я решил эту проблему для себя и опубликовал ответ ниже. Дайте мне знать, если вам понадобится дополнительная информация - это было немного болезненно, и я не хотел бы, чтобы кто-то другой переписывал все.

Ответы [ 7 ]

5 голосов
/ 13 апреля 2009

Могу ли я предложить несколько неортодоксальное решение?

Что бы вы подумали о размещении столбца «итого» после столбца «имя», а не в самом конце? Разве это не исключает необходимость прокручивать только часть таблицы?

Это не совсем то, что вы просите, но, возможно, это достаточное решение, учитывая, что альтернатива будет довольно грязной. (Например, размещение столбцов «total» и «name» вне таблицы создаст проблемы с выравниванием, когда не все строки имеют одинаковую высоту. Вы можете исправить это с помощью javascript, но тогда вы попадете в совершенно новый мир боль).

Кроме того, с точки зрения пользовательского интерфейса, может быть, что «имя» и «общее количество» являются наиболее важными данными, и в этом случае имеет смысл объединить их вместе, после чего следует своего рода «разбивка» общего количества. Конечно, у нас, кажется, есть интуиция, что «общее» должно следовать за его составными частями, но я не думаю, что это вызовет слишком большую путаницу для пользователя, если порядок будет изменен таким образом (хотя это вопрос для вы, на основе вашего продукта и ваших пользователей).

Во всяком случае, кое-что рассмотреть.

EDIT:

Вот еще несколько неортодоксальных решений, теперь, когда я думаю, что немного лучше понимаю ваши намерения:

  1. Разбейте результаты. Дайте самую последнюю десятку, скажем, и общее, и ссылка на более старые оценки, которые предоставляются 10 за один раз
  2. Дайте только имена, итоги и некоторые другие значимые показатели такие как Mean и SD, а затем предоставить ссылка для каждого имени, которое показывает все результаты, соответствующие этому название. Вы могли бы также предоставить ссылка, показывающая все результаты для заданный балл установлен, так что сравнения между разными пользователями может быть сделано. Дело в том, что вы бы нужно только дать 1 измерение данные для каждого представления, а не с громоздким набором 2D данных
  3. Сделайте строки сортируемыми (легко с jQuery UI), чтобы, если я хочу сравнить Мэри с Джейн, я могу перетащить и место один за другим, поэтому я не буду нужно продолжать прокручивать влево и право видеть, какие оценки соответствуют на какие имена
  4. Выделите строку, когда она нажата, изменив фон цвет или аналогичный, опять же, поэтому мне не нужно продолжать прокручивать влево и вправо.

В любом случае, вы поняли идею. Возможно, лучше искать решение для пользовательского интерфейса, чем искаженное решение для разметки. В конечном счете, я бы задался вопросом, насколько важно представлять пользователю столько данных одновременно, что часть его должна прокручиваться определенным образом, чтобы данные были читаемыми. Возможно, вы создаете приложение для работы с электронными таблицами, и вам действительно нужно отображать матрицу 100x100 в одном представлении. Если нет, вы наверняка могли бы придумать более креативные способы, чем я, чтобы разделить результаты.

3 голосов
/ 04 января 2011

Взгляните на этот плагин jQuery:

http://fixedheadertable.com/

2 голосов
/ 22 сентября 2009

Я экспериментировал с несколькими методами (спасибо всем, кто помог), и вот что я придумал, используя jQuery. Кажется, он хорошо работает во всех браузерах, которые я тестировал. Не стесняйтесь брать его и использовать его, как вы хотите. Следующим шагом для меня будет превращение его в повторно используемый плагин jQuery.

Резюме:

Я начал с обычной таблицы со всем в ней ( Id = "ladderTable" ) и написал Три метода - один для удаления первого столбца, один для удаления последний столбец и один для исправления высоты строки.

Метод stripFirstColumn создает новую таблицу ( Id = "nameTable" ), пересекает исходную таблицу и удаляет первый столбец и добавляет эти ячейки в nameTable.

Метод stripLastColumn делает в основном то же самое, за исключением того, что он удаляет последний столбец и добавляет ячейки в новую таблицу с именем totalTable .

Метод fixHeights просматривает каждую строку в каждой таблице, вычисляет максимальную высоту и применяет ее к связанным таблицам.

В событии готовности документа я вызвал все три метода по порядку. Обратите внимание, что все три таблицы смещены влево, поэтому они будут располагаться горизонтально.

Структура HTML:

<h1>Current Ladder</h1> 
<div id="nameTableSpan" style="float:left;width:100px;border-right:2px solid gray;"></div> 
<div id="ladderDiv" style="float:left;width:423px;overflow:auto;border:1px solid gray;margin-top:-1px;"> 
    <table id="ladderTable" class="ladderTable">
        <thead> 
            <tr><td>Name</td><td>Round 1</td> ... <td>Round 50</td><td class="scoreTotal">Total</td></tr>
        </thead>
        <tr><td>Bob</td><td>11</td> ... <td>75</td><td>421</td></tr>
           ... (more scores)
    </table>
</div>
<div id="totalTableSpan" style="float:left;width:70px;border-left:2px solid gray;"></div> 

JQuery:

function stripFirstColumn() {                
    // pull out first column:
    var nt = $('<table id="nameTable" cellpadding="3" cellspacing="0" style="width:100px;"></table>');
    $('#ladderTable tr').each(function(i)
    {
        nt.append('<tr><td style="color:'+$(this).children('td:first').css('color')+'">'+$(this).children('td:first').html()+'</td></tr>');
    });
    nt.appendTo('#nameTableSpan');
    // remove original first column
    $('#ladderTable tr').each(function(i)
    {
        $(this).children('td:first').remove();
    });
    $('#nameTable td:first').css('background-color','#8DB4B7');
}

function stripLastColumn() {                
    // pull out last column:
    var nt = $('<table id="totalTable" cellpadding="3" cellspacing="0" style="width:70px;"></table>');
    $('#ladderTable tr').each(function(i)
    {
        nt.append('<tr><td style="color:'+$(this).children('td:last').css('color')+'">'+$(this).children('td:last').html()+'</td></tr>');
    });
    nt.appendTo('#totalTableSpan');
    // remove original last column
    $('#ladderTable tr').each(function(i)
    {
        $(this).children('td:last').remove();
    });
    $('#totalTable td:first').css('background-color','#8DB4B7');
}

function fixHeights() {
    // change heights:
    var curRow = 1;
    $('#ladderTable tr').each(function(i){
        // get heights
        var c1 = $('#nameTable tr:nth-child('+curRow+')').height();    // column 1
        var c2 = $(this).height();    // column 2
        var c3 = $('#totalTable tr:nth-child('+curRow+')').height();    // column 3
        var maxHeight = Math.max(c1, Math.max(c2, c3));

        //$('#log').append('Row '+curRow+' c1=' + c1 +' c2=' + c2 +' c3=' + c3 +'  max height = '+maxHeight+'<br/>');

        // set heights
        //$('#nameTable tr:nth-child('+curRow+')').height(maxHeight);
        $('#nameTable tr:nth-child('+curRow+') td:first').height(maxHeight);
        //$('#log').append('NameTable: '+$('#nameTable tr:nth-child('+curRow+')').height()+'<br/>');
        //$(this).height(maxHeight);
        $(this).children('td:first').height(maxHeight);
        //$('#log').append('MainTable: '+$(this).height()+'<br/>');
        //$('#totalTable tr:nth-child('+curRow+')').height(maxHeight);
        $('#totalTable tr:nth-child('+curRow+') td:first').height(maxHeight);
        //$('#log').append('TotalTable: '+$('#totalTable tr:nth-child('+curRow+')').height()+'<br/>');

        curRow++;
    });

    if ($.browser.msie)
        $('#ladderDiv').height($('#ladderDiv').height()+18);
}

$(document).ready(function() {
    stripFirstColumn();
    stripLastColumn();
    fixHeights();
    $("#ladderDiv").attr('scrollLeft', $("#ladderDiv").attr('scrollWidth'));    // scroll to the last round
});

Если у вас есть какие-либо вопросы или есть что-то неясное, я более чем рад помочь.

Мне потребовалось довольно много времени, чтобы понять, что я ничего не могу по-настоящему использовать, и мне потребовалось немного больше времени, чтобы написать это. Я бы не хотел, чтобы кто-то пошел на такую ​​же неприятность.

2 голосов
/ 13 апреля 2009

Пока это не завершено (я просто очень быстро смоделировал это), но вот способ сделать это, используя прямой HTML. Вы хотите три стола ... два вне

и один внутри
. Все три смещены влево, так что все они находятся на одной линии.

Таблица испытаний

и сам код:

  <table style="float: left;">
     <tr>
        <td>Name</td>
     </tr>
     <tr>
        <td>John</td>
     </tr>
     <tr>
        <td>Will</td>
     </tr>
  </table>
  <div id="tableWrapper" style="overflow-x: auto; width: 500px;float:left;padding-bottom: 10px;">
      <table>
        <tr>
           <td>Score1</td>
           <td>Score2</td>
           ...
        </tr>
        <tr>
           <td>5</td>
           <td>6</td>
           ...
        </tr>
        <tr>
           <td>3</td>
           <td>7</td>
           ...
        </tr>
        <tr>
           <td>7</td>
           <td>1</td>
           ...
        </tr>
      </table>
  </div>
  <table style="float: left;">
     <tr>
        <td>Total</td>
     </tr>
     <tr>
        <td>86</td>
     </tr>
     <tr>
        <td>82</td>
     </tr>
  </table>

Примечание. Нижний отступ элемента div предназначен для того, чтобы полоса прокрутки не закрывала таблицу в IE. Кроме того, ошибка, которую я должен решить, состоит в том, как указать ширину элементов заголовка внутри средней таблицы. По какой-то причине указание атрибута width = "" не работает. Таким образом, если текст элемента заголовка слишком широкий (и разбивается на другую строку), то макет разбивается (на одну строку)

2 голосов
/ 13 апреля 2009

Полагаю, вы хотите прокрутить его только по горизонтали. В противном случае это может привести к путанице со статическими столбцами.

Вы можете поместить overflow: auto на содержащий элемент только в ячейках внутренней таблицы ... Я не уверен, как браузеры справятся с этим, но вы можете поместить элементы, которые вы хотите исправить, в thead и tfoot. элементы, и поместите прокручиваемую часть в элемент tbody, и установите его переполнение на auto.

В противном случае вам может понадобиться удалить семантику и кодировать левый и правый столбцы за пределами таблицы.

Лично я бы попробовал закодировать его как можно более семантически, а затем использовать JavaScript для позиционирования левого и правого столбцов. Без JavaScript вы должны сделать так, чтобы он просто не подходил к широкой таблице (не уверен, насколько это будет сложно, так как вы говорите, что у вас фиксированная ширина)

Вы можете попробовать этот (грубый) пример jQuery, чтобы поместить значения первого столбца снаружи.

var nameTable = '<table id="outside-name-col">
                  <thead>
                   <tr>
                    <th>Name</th>
                   </tr>
                  </thead>
                 <tbody>'; // start making a table for name column only

$('#scoreTable tbody tr').each(function() { // iterate through existing table

   var nameCol = $(this).find(':first'); // get column of index 0, i.e. first

   var cellHeight = nameCol.height(); // get the height of this cell

   $(this).find('td').height(cellHeight); // equalise the height across the row so removing this element will not collapse the height if it is taller than the scores and total       

   nameTable += '<tr><td style="height: ' + cellHeight + 'px">' + nameCol.html() + '</td></tr>'; // append the next row with new height and data

   nameCol.remove(); // remove this cell from the table now it's been placed outside

});

nameTable += '</tbody></table>'; // finish table string


$('#scoreTable').before(nameTable); // insert just before the score table

Используйте CSS для их правильного выравнивания. Это не проверено, но оно должно дать вам некоторое представление.

1 голос
/ 13 апреля 2009

Вы имели ввиду горизонтальную прокрутку ??

Если вы хотите добиться горизонтальной прокрутки, вы можете использовать 3 контейнера.

  1. Для первого столбца (Имя)
  2. Для столбцов, которые вы хотите прокрутить. Установите для стиля контейнера overflow-x значение auto
  3. Для последнего столбца (Всего)
0 голосов
/ 13 апреля 2009

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

Лучший и самый безопасный способ сделать это требует JavaScript. Вы бы легко достигли даже горизонтальной прокрутки (это бесплатно) с помощью этой техники, которую я использовал. Вы можете легко преобразовать это в свою проблему:

1) Я сделал 3 таблицы

2) Поместил среднюю таблицу, в которой хранятся данные, внутри контейнера DIV

3) установить переполнение стиля DIV: авто

4) дал таблицу верхнего и нижнего колонтитула и div шириной 100%

.

5) окружил все другим div для контроля общей ширины таблицы

6) удостоверился, что количество столбцов во всех трех таблицах совпадает и что все три имеют одинаковые стили в отношении отступов, полей и границ

Теперь интересная часть:

6) под последним элементом этой конструкции таблицы напишите блок JavaScript (или включите его из файла)

7) написать функцию, которая последовательно просматривает каждый столбец (используйте цикл for), начиная с первого. На каждой итерации этого цикла проверяйте ячейку td с первыми двумя таблицами в текущем столбце. Проверьте, какой из них больше, и назначьте его стилю меньшей ячейки TD. Вы можете также применить это значение к таблице нижнего колонтитула.

8) вызвать эту функцию. Возможно, вы захотите сделать это через определенный промежуток времени или при возникновении особых событий, таких как новые данные в ячейках (например, при использовании ajax).

Примечание. При горизонтальной прокрутке размеры столбцов синхронизируются. Но вам придется синхронизировать позицию прокрутки таблицы верхнего и нижнего колонтитула с таблицей содержимого. В качестве альтернативы вы просто позволяете полному div прокручивать все по горизонтали, но не по вертикали. Это будет работа DIV, которая содержит таблицу данных.

Этот метод очень хорошо работал во всех основных браузерах в проекте с диаграммой Гопта очень коплекс.

Примечание. Вы можете легко представить себе эту концепцию с поворотом на 90 градусов, чтобы при прокрутке фиксировать первый и последний столбец заголовка.

Редактировать: Вот еще один пример из моей базы кода, который может помочь вам найти решение:

var left_td = document.getElementById("left_td");
var right_td = document.getElementById("right_td");
var contentDiv = document.getElementById("contentDiv");
window.setInterval(function(){
    var left_td_width = Math.round((parseInt(contentDIV.offsetWidth) - 120) / 2);
    var right_td_width = (parseInt(contentDIV.offsetWidth) - left_td_width - 120;

    left_td.style.width = left_td_width + "px";
    right_td.style.width = right_td_width + "px";
}, 25);

Этот код выполняет следующие действия: он гарантирует, что таблица с 3 столбцами всегда выглядит так: первый и последний столбцы имеют одинаковый размер, а центральный столбец имеет ширину 120 пикселей. Конечно, это не относится непосредственно к вашей особой проблеме, но может дать вам отправную точку в динамическом манипулировании таблицами с помощью JavaScript.

...