SELECT-таблицы: эффективный подход к работе с данными в табличной форме

01.08.2012
Разрабатывая очередной Интернет-магазин, я столкнулся с проблемой низкоскоростного вывода данных в табличной форме. Суть задачи состояла в том, чтобы отображать информацию целым блоком по 300-500 записей, а не делить их на страницы. Преимущество постраничного вывода одно – скорость вывода порции информации. Отображение HTML-таблицы с 30-50 записями осуществляется заметно быстрее отображения таблицы с 300 записями. Но у постраничного вывода есть существенный недостаток – низкая эффективность работы при большом количестве данных в выборке. Предположим, что по запросу было найдено 150 записей, а вывод данных осуществляется по 30 записей на страницу. Получается  5 страниц и в самом неблагоприятном случае посетителю придётся выполнить 5 щелчков для загрузки каждой страницы, потому что он не знает, на какой из них находится интересующая его информация. Я часто сталкиваюсь с такой ситуацией при заказе товара в интернет-магазинах, когда приходится совершать большое количество лишних и ненужных движений при поиске информации. С информацией на одной странице работать намного удобнее. Можно пользоваться функцией поиска браузеров и быстро просмотреть всю таблицу. Но если в таблице выводится большое количество приличных по размеру изображений товаров, то отображение браузером такой таблицы будет осуществляться очень долго. Что же делать? Искать и экспериментировать.
Пример 1. HTML-таблица
<table width="0" border="1" cellpadding="0" cellspacing="0">
 <tr>
  <td>Ячейка 1-1</td>
  <td>Ячейка 1-2</td>
  <td>Ячейка 1-3</td>
  <td>Ячейка 1-4</td>
 </tr>
 <tr>
  <td>Ячейка 2-1</td>
  <td>Ячейка 2-2</td>
  <td>Ячейка 2-3</td>
  <td>Ячейка 2-4</td>
 </tr>
</table>
Результат
Ячейка 1-1 Ячейка 1-2 Ячейка 1-3 Ячейка 1-4
Ячейка 2-1 Ячейка 2-2 Ячейка 2-3 Ячейка 2-4
Табличный вывод данных можно реализовать при помощи специальных табличных свойств каскадных таблиц стилей.
Пример 2. CSS-таблица
<style type="text/css">
#table {display: table;width:300px;border:1px solid black;border-collapse:collapse} .row {display: table-row;border:1px solid black; }
.cell {display: table-cell;border:1px solid #ff0000;}
</style> <div id="table"> <div class="row"> <span class="cell">Ячейка 1-1</span> <span class="cell">Ячейка 1-2</span> <span class="cell">Ячейка 1-3</span> <span class="cell">Ячейка 1-4</span> </div> <div class="row"> <span class="cell">Ячейка 2-1</span> <span class="cell">Ячейка 2-2</span> <span class="cell">Ячейка 2-3</span> <span class="cell">Ячейка 2-4</span> </div> </div>
Результат
Ячейка 1-1 Ячейка 1-2 Ячейка 1-3 Ячейка 1-4
Ячейка 2-1 Ячейка 2-2 Ячейка 2-3 Ячейка 2-4
Преимущество CSS-таблиц заключается в возможности отделения структуры данных с самими данными и более тонкой настройки параметров элементов таблицы, но они отображаются также медленно, как и HTML-таблицы.
Для увеличения скорости отображения данных можно воспользоваться слоями и создать DIV-таблицу.
Пример 3. DIV-таблица
<style type="text/css">
 .line1 {width:500px;line-height:33px;height:33px;}
 .menu1 div{float:left;border:1px solid #000;margin:1px 1px 0px 0px;}
 .m11{width:100px}
 .m12{width:100px}
 .m13{width:100px}
 .m14{width:100px}
</style>

<div class="line1">
 <div class="menu1">
  <div class="m11">Ячейка 1-1</div>
  <div class="m12">Ячейка 1-2</div>
  <div class="m13">Ячейка 1-3</div>
  <div class="m14">Ячейка 1-4</div>
 </div>
</div>
<div class="line1">
 <div class="menu1">
  <div class="m11">Ячейка 2-1</div>
  <div class="m12">Ячейка 2-2</div>
  <div class="m13">Ячейка 2-3</div>
  <div class="m14">Ячейка 2-4</div>
 </div>
</div>
Результат
DIV-таблицы на порядок сложнее в настройке по сравнению с HTML- и CSS-таблицами, но небольшое количество записей отображают заметно быстрее последних, так как слои отображаются сразу, а не после формирования всей структуры, как это происходит в случае с HTML- и CSS-таблицами.
С увеличением количества записей  DIV-таблица начинает “плавать” по причине того, что браузер не успевает одномоментно отобразить большое количество слоёв и начинает подтормаживать.
После небольшого эксперимента становится понятно, что указанные выше способы не могут принципиально увеличить скорость отображения большого количества данных в табличной форме. Тогда я решил сравнить скорости вывода сплошного массива информации различными способами, чтобы понять, а вообще существует ли объект, который способен быстро отображать табличную информацию?
Метод document.write (JavaScript) выводит информацию на страницу быстрее HTML-таблиц, но тут надо ещё подумать, как организовать табличный вывод, как и в случае с объектом формы TEXTAREA. А вот объект формы SELECT (список) показался весьма интересным. Он очень быстро заполняется большими объёмами данных, выводит их построчно и предоставляет в распоряжение разработчика готовый механизм для работы с этими данными: перемещение по записям, определение текущей записи, получение данных записи по заданному номеру, возможность выделения нескольких записей и др. Для получения таблицы из списка осталось придумать механизм деления информации на ячейки.
Элементы формы визуально перекрывают HTML-элементы, поэтому в качестве вертикального разделителя строк списка также используется элемент формы SELECT, настроенный определенным образом для получения тонкой линии. Визуально таблица из списка получилась, а как выводить в неё информацию? Для этого необходимо выбрать моноширинный шрифт (Lucida Console) и при помощи символа заполнения производить выравнивание строк по количеству помещающихся в ячейку символов:
Яче!!!1+++Ячейка2+++Ячейка3
sel_1+++++GOOD2+++++Яче3
SEL_1: 1++яЧеЙкА21++я3
, где “+” - заполняющий символ, который в реальном примере заменён на символ пробела.
Благодаря моноширинному шрифту получаются строки одинаковой длины, а при помощи заполняющего символа производится деление строки на фрагменты, которые отображаются в ячейках таблицы. То есть, в список SELECT выводятся строки, разделённые пробелами, что визуально выглядит как таблица.
Понятно, что длинная строка не поместится в ячейку таблицы и будет урезана, поэтому исходные данные без усечения хранятся в javascript-массиве, из которого и производится заполнение SELECT-таблицы. Однозначное соответствие записей в массиве и таблице осуществляется либо по порядковому номеру, либо по коду записи в таблице. Можно сделать так, чтобы идентификатор записи в таблице не отображался.
Пример 4. SELECT-таблица (список-таблица)
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=windows-1251" />
<title>Документ без названия</title>
<style type="text/css">
 .tr1{position:absolute;left:45px;WIDTH: 1px;margin-top:0px;overflow:hidden;height:70px;border-width:1px;}
 .tr2{position:absolute;left:453px;WIDTH: 1px;margin-top:0px;overflow:hidden;height:70px;border-width:1px;}
 .tr3{position:absolute;left:501px;WIDTH: 1px;margin-top:0px;overflow:hidden;height:70px;border-width:1px;}
 .t1{float:left;width:45px;text-align:center;height:20px;background-color:#999;color:#FFF}
 .t2{float:left;width:408px;text-align:center;height:20px;background-color:#999;color:#FFF}
 .t3{float:left;width:48px;text-align:center;height:20px;background-color:#999;color:#FFF}
.t4{float:left;width:299px;text-align:center;height:20px;background-color:#999;color:#FFF} .select{height:50px;border:0px solid #900; width:800px;font-size: 14px;font-family: Lucida Console;overflow: hidden} </style> <script type="text/javascript"> var U_Catalog=new fCatalog(); function fCatalog() { this.Data= new Array(); } function fCatalogRecord(A_Code,A_Name,A_Cost, A_Info) { this.Code=A_Code; this.Name=A_Name; this.Cost=A_Cost; this.Info=A_Info; } function AS_GetSpace(A_Value,A_Sp) { var L_Result=''; for(var L_i=0;L_i<A_Value;L_i++) L_Result=L_Result+'&nbsp;'; return L_Result; } function AS_Format() { var L_Arr=new Array(5,50,5); var L_Sp=new Array(1,1,1); var L_Result=''; var L_Length=arguments.length; var L_Str=''; for(var L_i=0;L_i<L_Length;L_i++) { if ((L_i==1)&&(arguments[L_i]==0)) arguments[L_i]=''; if (arguments[L_i].length>L_Arr[L_i]) L_Str=arguments[L_i].substr(0,L_Arr[L_i])+''; else L_Str=arguments[L_i]+''; L_Result=L_Result+L_Str+AS_GetSpace(L_Arr[L_i]-L_Str.length)+AS_GetSpace(L_Sp[L_i]); } return L_Result; } function AS_Load() { var L_i=U_Catalog.Data.length; U_Catalog.Data[L_i]=new fCatalogRecord("1-1","Ячейка 1-2. Это очень длинное название товара в ячейке 1","1-3","Описание 1-4"); U_1=AS_Format(U_Catalog.Data[L_i].Code,U_Catalog.Data[L_i].Name,U_Catalog.Data[L_i].Cost, U_Catalog.Data[L_i].Info); document.getElementById("idOpt1").innerHTML=U_1; U_Catalog.Data[L_i]=new fCatalogRecord("2-1","Ячейка 2-2. Это название товара в ячейке 2","2-3","Описание 2-4"); U_1=AS_Format(U_Catalog.Data[L_i].Code,U_Catalog.Data[L_i].Name,U_Catalog.Data[L_i].Cost, U_Catalog.Data[L_i].Info); document.getElementById("idOpt2").innerHTML=U_1; U_Catalog.Data[L_i]=new fCatalogRecord("123456789","Ячейка 3-2. Это название товара в ячейке 3","3-3","Описание 3-4"); U_1=AS_Format(U_Catalog.Data[L_i].Code,U_Catalog.Data[L_i].Name,U_Catalog.Data[L_i].Cost, U_Catalog.Data[L_i].Info); document.getElementById("idOpt3").innerHTML=U_1; document.getElementById("idCatalog").selectedIndex=0; } </script> </head> <body onload="AS_Load()"> <div id="idAll" style="position:relative;width:800px;border:1px solid #000"> <div style="border:0px solid #900;font:13px arial;"> <SELECT id="idTL1" class="tr1" size="3" disabled="disabled"></SELECT> <SELECT id="idTL2" class="tr2" size="3" disabled="disabled"></SELECT> <SELECT id="idTL3" class="tr3" size="3" disabled="disabled"></SELECT> <div class="t1">Код</div> <div class="t2">Наименование</div> <div class="t3">Цена</div> <div class="t4">Описание</div> </div> <SELECT id="idCatalog" multiple class="select" size="3" style="background-color:#CCC"> <option id="idOpt1"></option> <option id="idOpt2"></option> <option id="idOpt3"></option> </SELECT> </div> </body> </html>
Результат:
Код Наименование Цена Описание
Преимущества SELECT-таблиц
  • Высокая скорость заполнения данными
  • Высокая скорость работы с данными (перемещение по записям, поиск и т.п.)
  • Интерактивность
  • Существенное сокращение объема передаваемых с сервера данных, так как происходит передача только данных без тегов форматирования
  • Возможность работы с данными в реальном времени
  • Простота работы пользователей с таблицей
  • Компактность расположения на странице
  • Простота визуального оформления таблицы
  • Возможность эффективной реализации механизма с предзагрузкой и постоянной загрузкой данных
Недостатки SELECT-таблиц:
  • Сложная настройка и необходимость программирования в процессе создания таблицы
  • Невозможность отображения в ячейках графических данных
  • Нет возможности красивого оформления данных в ячейках
  • Сложности с реализацией механизма интерактивного изменения размеров таблицы и ячеек
AJAX-технология не является принципиально новой идеей, так как загрузку данных без перезагрузки HTML-страниц можно легко выполнить при помощи плавающего фрейма. Одно из преимуществ AJAX заключается в том, что эта технология при использовании относительно быстрого канала связи позволяет весьма эффективно работать и с обычными HTML-таблицами, не говоря уже о работе с SELECT-таблицами!
Скорость вывода табличных данных зависит не только от скорости их отображения браузером, но и от объёма данных. В большинстве случае табличные данные выводятся путем разового вывода определенной порции данных. Например, в таблицу выводятся все записи из выбранного щелчком мыши раздела товаров. Если в разделе 10 записей, то выводится таблица с 10-ю записями, если 100, то выводится таблица со ста записями. Если записи не помещаются в видимой обоасти окна браузера, то пользователь осуществляет прокрутку страницы для просмотра скрытых записей таблицы. Это похоже на то, как работает Excel. А вот работа с информацией в базах данных осуществляется иначе: пользователь постоянно видит определенное количество записей в таблице, а перемещение по записям таблиц производится путём прокрутки записей, а не самой таблицы! То есть, на страницу можно вывести HTML-таблицу с 20-ю записями и при помощи асинхронного канала AJAX подгружать в неё данные. Браузеры в состоянии быстро перерисовывать HTML-таблицы c 20-ю записями, и общая скорость отображения данных уже в значительно меньшей степени будет зависеть от скорости отображения HTML-таблицы.
Можно не перерисовывать всю таблицу, а при помощи DHTML вставлять полученные данные в её ячейки. Если нужно добиться ещё большей оптимизации, то можно при прокрутке таблицы на одну запись сдвигать все строк в массиве на одну позицию вверх или вниз, а с сервера запрашивать только одну новую запись, что позволит значительно сократить объем пересылаемых данных. Как вариант, можно предварительно загрузить все записи из базы данных на сервере в локальный массив, что ещё больше ускорит работу.
Благодаря AJAX традиционный web-сайт можно превратить в web-приложение, которое чём-то будет напоминать традиционное Windows-приложение. А нужно ли это? WEB-сообщество постоянно стремится придумать нечто своё особенное, а здесь получается возврат к унылым и однообразным серым окнам. Разве к этому мы стремились, воскликнут дизайнеры! Если традиционное программирование предлагает эффективные и понятные многим механизмы работы интернет-магазинов, то почему нужно от них отказываться во имя стиля и продолжать биться головой об стену?
Парадокс, но именно разработчики WEB-технологий и браузеров тормозят развитие ряда направлений WEB. HTML 5, XML, CSS 3, Flash, виртуальная реальность и мн. др. - это всё прекрасно, но сначала дайте нам, web-разработчикам и пользователям, нормальный табличный объект, который мы ждём уже "надцать" лет и с чувством выполненного долга разрабатывайте хоть 5-ти мерный WEB!
Оглавление
Copyright © 2016