Отправляет email-рассылки с помощью сервиса Sendsay
  Все выпуски  

Подсветка и выделение строк таблиц. Структуры данных в таблицах и операции с ними.


краткое содержание

Подсветка и выделение строк таблиц. Структуры данных в таблицах и операции с ними.

Изменение цвета строк таблицы при наведении/клике вместе с чекбоксами на них. Отображение многоуровневой структуры данных в таблице и выделение подструктур кликом на строке-заголовке. Скрывание/показ подструктур. (2) Новый сервис сайта: "Вопрос-ответ по Javascript".
Существует дней: 55
Автор: 12345c
Другие выпуски:
Рассылка 'Упражнения по яванскому письму. Javascript.'
 
Статья.
11.06.06

Таблицы с подсветкой строк. Часть 2.

Продолжение показа вариантов реализации подсветки и работа с многоуровневыми структурами данных, отображённых в таблицах. Выделение подсписка по строке заголовка, сворачивание или показ подсписков. Генерация таблицы из более компактного скриптового представления данных, которое более наглядно для заполнения и которое удобнее заполнять из БД на сервере или передавать части таблиц, оперируя подструктурами данных.

В предыдущем выпуске был приведён пример с кодом скрипта выделения и подсветки строк, основанном на стилях псевдоклассов в FF/Opera и behavior-скриптах в IE.

Нельзя сказать, что этот код перспективен для развития и эффективен для применения. Для работы он требует 2 запросов к серверу вместо одного, чем если бы он был сделан на скриптах. В нём нельзя сделать "шлейф" подсветки строк, не изменив подход к написанию. Выделения на псевдоклассах не выполняются (:active), а помогают лишь подсветить ячейку в IE во время нажатия. Поэтому выделения сделаны на скриптах.

Удобнее, всё же, несмотря на неоригинальность, делать подсветку целиком на скриптах. В ней возникает 2 варианта слежения за событиями: в атрибутах тегов или функциями-обработчиками событий. Если тело таблицы генерируется скриптом, то нет особой разницы, как реакция на события будет организована: исходный код будет одинаково компактным.

Вспомним из предыдущей статьи о том, какие варианты задач могут быть при реализации скрипта. Упоминались 3 плоскости (аспекта) вариаций алгоритма: по реализации подсветок, требуемым анимационным эффектам, функциональности. Расклассифицируем возможные технические варианты исполнения таблиц в зависимости от поставленной задачи. Конечно, конкретных вариантов может быть больше, но данная таблица будет охватывать наиболее типичные.

1. Варианты реализации подсветок
a) На псевдоклассах и behavior
b) На функциях-атрибутах тегов
c) на функциях-обработчиках событий
2. Эффекты выделения строк таблиц
1) Чётные/нечётные выделяются разными цветами
2) Простая подсветка при наведении мыши
3) Дополнительная подсветка при клике
4) Шлейф при движениях мыши
5) Синхронное выделение строки и чекбокса или радиокнопки на ней
6) Сворачивание/разворачивание подсписка по клику на строке, другие действия по клику на ячейке строки
3. Функциональность данных в таблицах
a) Простой список
b) Список со строкой "выделить/очистить всё"
c) Многоуровневая структура данных, отображённая в строках (типа содержания книги с разделами, главами, параграфами)
d) Расположение элемента данных в подстроке таблицы (таблица с 2 и более элементами на строку)

Буквенными индексами указаны альтернативы, числовыми - независимые свойства таблиц. Эти свойства могут комбинироваться в конкретной реализации таблицы, и необязательно, чтобы скрипт обеспечивал возможность задействовать их все. Меньше возможностей - проще скрипт, быстрее его работа, проще его настройка.

В написании примеров к статье принимали участие 2 других автора, кроме автора рассылки: codexomega, Ve-ve. Они предоставили свои варианты реализации подсветки строк в таблицах. Рассмотрим эти варианты.

Код codexomega выполнен на скриптах, со строкой "выделить всё" в таблице. Чересполосица строк задана в таблице, к скрипту не имеет отношения. В скрипте удачно задействована приоритетность свойства стиля backgroundColor перед свойством тега bgСolor, что сокращает код. Клик по верхней строке не приводит к выделению чекбокса, хотя логично было бы выделять, как во всех нижних строках.

<style>
 .highlight{ background-color: #1E90FF}
 .light{ background-color: #FFFFFF}
 .dark{ background-color: #CCCCCC}
</style>
<script>  // © codexomega
function checkAll(field){
  nb_checked=0;
  for(n=0;n<field.length;n++)
    if(field[n].checked)nb_checked++; 
    if(nb_checked==field.length){ 
      for(j=0;j<field.length;j++){ 
        field[j].checked=!field[j].checked; 
        field[j].parentNode.parentNode.style.backgroundColor
          =field[j].backgroundColor==''?'#1E90FF':'';
      }
    }else{
      for(j=0;j<field.length;j++){
        field[j].checked = true;
        field[j].parentNode.parentNode.style.backgroundColor
          ='#1E90FF';
      }document.frm_data.check_all.checked=true;
    }
}
function selectRow(evnt,elmnt){
  var ch=elmnt.getElementsByTagName("TD")[0].firstChild;
  tg = document.all?evnt.srcElement:evnt.target;
  if(tg.tagName!='INPUT')ch.checked=!ch.checked;
  elmnt.style.backgroundColor=ch.checked?'#1E90FF':'';
}
</script>
<div><form name=frm_data id=frm_data>
<table width=100% border=1 cellpadding=3 cellspacing=0>
  <tr bgcolor=000066>
  <th width=10% ><input type=checkbox name=check_all
     value=check_all
     onclick=checkAll(document.frm_data.deletelist)></th>
  <th width=40% style="color:#FFFFFF">Airplane Name</th>
  <th width=40% style="color:#FFFFFF">Airplane Type</th>
</tr></table>
<div>
<table class=list width=100%  border=1 cellpadding=3
   cellspacing=0>
    <tr class=light onMouseOut=this.className='light'
       onMouseOver=this.className='highlight'
       onclick=selectRow(event,this)>
    <td width=10% align=center ><input type=checkbox
       name=deletelist[1] id=deletelist></td>
    <td width=40% align=center>B2-Spirit</td>
    <td width=40% align=center>Bomber</td>
   </tr>
  <tr class=dark onMouseOut=this.className='dark'
     onMouseOver=this.className='highlight'
     onclick=selectRow(event,this)>
    <td width=10% align=center ><input type=checkbox
      name=deletelist[2] id=deletelist></td>
    <td width=40% align=center>F15-Eagle</td>
    <td width=40% align=center>Fighter</td>
   </tr>
  <tr class=light onMouseOut=this.className='light'
     onMouseOver=this.className='highlight'
     onclick=selectRow(event,this)>
    <td width=10% align=center ><input type=checkbox
       name=deletelist[3] id=deletelist></td>
    <td width=40% align=center>Mig29-Fulcrum</td>
    <td width=40% align=center>Fighter</td>
</tr></table>
</div></form></div>

Скрипт имеет 2 функции, отвечающие за 2 заложенные функциональности - выделение всех строк и подсветку. Выделение всех строк, как можно проверить по примеру, выполнено по логике: если были выделены не все строки или не выделены, нажатие чекбокса "Выделить всё" приводит в выделению. Если были выделены все, нажатие чекбокса приводит к сбросу выделений. Реализовано в функции checkAll() через 2 прохода списка чекбоксов (массива deletelist[]) - для проверки и для установки.

Вторая функция selectRow() работает для выделения строк при кликах мыши. Выделение при движениях выполнено в коде атрибутов тегов, как, например, onMouseOver=this.className='highlight'.

Достаточную простоту скрипта по сравнению со следующим, с дополнительными функциями, следует рассматривать как достоинство. Его проще интегрировать с другими функциями страницы для вебмастера, пожелавшего воспользоваться скриптом.

Скрипт Ve-ve имеет эффект шлейфа подсветки, генерацию таблицы из функций Javascript, генерируемую чересполосицу строк, сопровождается комментариями, поэтому будет достаточно хорош для использования и разбора решения.

<script> // © Ve-ve
var t='<form name = "qform" action = blabla>'
+'<table id = "qtable" >';  
var i= 0; var c=0;   var trs=new Array(); 
function add_tr(txt){ /* Этой функцией добавляем новую
  строчку в таблице. В качестве аргументов идет массив
  с нужными элементами. Количество элементов массива
  равно количеству столбцов */
i++; 
t+='<tr onmouseover = "this.className=this.className.'
  +'replace(\'n\',\'o\')"';
t+='  onmouseout = window.setTimeout(\'lol("\'+this.id'
  +'+\'")\',100); ';
t+=' colorthis=1 id="qe'+i+'" class = "en'+(i%2)+'">';
t+=(i==1?'<td style="width:0"><input   type = "checkbox"'
  +' onclick = "ch_all(this)" border= 10 id="s_all">'
  :'<td ><input onclick=r_ch("qe'+i
  +'")  type=checkbox name = "v[]" id = "chbx'+i+'" >');
trs[i]='qe'+i; 
for(e in txt){ 
onc='onclick=document.forms[\'qform\'].elements['
  +(i-1)+'].checked=!document.forms[\'qform\'].elements['
  +(i-1) +'].checked;r_ch("qe'+i+'")';
t+='<td '+(i>1?onc:'')+' id = "t_id'+i+'_'
  +e+'" class="td_'+e+'_'+(i%2)+'" nextclass>'+txt[e];}
}
function lol(th){document.getElementById(th).className
  =document.getElementById(th).className.replace('o'
  ,'n');}
function r_ch(tr_id,act){ 
/*Меняем имя класса ячейки, пришедшей первым параметром,
дабы перекрасить ее. Новое имя класса или вычисляем из
значения текущего класса или берем из второго
необязательного параметра, если он есть*/
  j=document.getElementById(tr_id).className;
  document.getElementById(tr_id).className
  =j.replace(j.charAt(0),j.charAt(0)=='e'?'f':'e');
(j.charAt(0)=='e'?c++:c--);
    document.forms['qform'].s_all.checked=i==(c+1) 
}
function ch_all(ch_id){ 
/*Функция запускается по нажатию главного квадратика
(выделить все)) Выделяем все строчки.*/
    k=(i!=(c+1)); /*Решаем,что будем делать с чекбоксами,
     выбирая значение переменной k, которое в результате
     будем присваивать всем найденным чекбоксам. Если
     количество выделенных равно общему количеству,
     значение к=false, иначе к=true.*/
    ch_id.checked=k  // Устанавливаем значение для
      //квадратика "выделить все"

    for(e=1;e<i+1;e++)//Обходим все строчки и чекбоксы
    {tr=document.getElementById(trs[e]);//находим строчку
      j=tr.className;
        tr.className=j.replace(j.charAt(0),(k?'f':'e'));
          //имя класса в зав. от перем. k
        chb=document.forms['qform'].elements[e]; 
        if(chb){ 
            c+=(k-chb.checked);
// Увеличиваем или уменьшаем кол-во выделенных чекбоксов
            chb.checked=k;
     // Присваиваем нужному квадратику нужное значение
}}}
add_tr(['0',' Строка 0 '])
add_tr(['1',' Строка 1 '])
add_tr(['2',' Строка 2 '])
add_tr(['3',' Строка ***3 '])
add_tr(['4',' Строка 4 '])
add_tr(['5',' Строка 5 '])
add_tr(['6',' Строка 6 '])
add_tr(['7',' Строка **7 '])
add_tr(['8',' Строка 8 '])
add_tr(['9',' Строка 9 '])

t+='</table></form>';
document.write(t);
function firef(){  for(e=1;e<i+1;e++){
    tr=document.getElementById(trs[e+1]); 
    chb=document.forms['qform'].elements[e]; 
    if(chb){if(chb.checked){j=tr.className;
        tr.className=j.replace(j.charAt(0),'f');
        c++;
}}}}
</script>
<style>
td{padding:2;font-family:verdana;font-size:11;}
#qtable{border-collapse: collapse;width:100%;}
#t_id1_0{color:white;font-weight:bold;}
#t_id1_1{color:white;font-weight:bold;}
#qe1{background-color:maroon} /* Красим заголовок*/
 .en1{background-color:dddddd} /* Красим нечетные яч*/
 .en0{background-color:aaaaaa} /* Красим нечетные яч*/
 .fo1,.fn1,.eo1{background-color:dddd00}/*Красим нечетные*/
 .fo0,.fn0,.eo0{background-color:aaaa00}/*Красим нечетные*/

 .td_0_0,.td_0_1{width:34%} /* устанавливаем размеры
           и свойства ячеек в первом столбце */ 
 .td_1_0,.td_1_1{width:60%} /* во втором столбце */ 
</style>
<body onload="firef()">

Результаты, согласитесь, интереснее, но разбирать и модифицировать этот скрипт удобно уже опытному программисту. Дам небольшой полезный совет для разбора примера. Чтобы увидеть, что скрипт нагенерировал, вставьте в начале кода функцию, превращающую document.write() в функцию печати кодов:

document.write=function(s){
  document.writeln(s.replace(/</g,"&lt;")
    .replace(/\n/g,"<br><br>"));}

Для отметки конца строк в кодах должны быть, конечно, "\n". Их сейчас нет, соотвественно, показанный код будет выведен в одну строку. (В кодах и </tr> нет, что не есть хороший тон, хотя работает.)

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

Наконец, рассмотрим ещё один скрипт Ve-ve, который позволяет генерировать таблицу, отражающую структуру данных. По данным, заданным в скрипте, генерируются заголовки, списки, подзаголовки, подсписки. По аналогии с мультиселектом, рассмотренным в прежнем выпуске, по его терминологии можем сказать, что строки заголовков относятся к "пустым" узлам (не содержащим информации иной, кроме как о своём подсписке). Поэтому чекбокс заголовка всегда отражает выбранность элементов своего подсписка.

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

<body> <!-- Стили разных уровней структуры-->
<style> div{font-family:verdana;font-size:10}
 .an01,.an11{background-color:#dddddd;color:black}
  .an02,.an12{background-color:#bbbbbb;color:black}
    .an03,.an13{background-color:#999999;color:black}
      .an04,.an14{background-color:#888888;color:black}
 .ao01,.ao11{background-color:#dd9933;color:black}
  .ao02,.ao12{background-color:#997711;color:black}
    .ao03,.ao13{background-color:#775500;color:black}
      .ao04,.ao14{background-color:#ee7700;color:black}
 .bn01,.bn11,.bn02,.bn12,.bn03,.bn13,.bn04,.bn14
  {background-color:#998899;color:white}
 .bo01,.bo11,.bo02,.bo12,.bo03,.bo13,.bo04,.bo14
  {background-color:#884488;color:white}
</style>
<script> // © Ve-ve
/*  document.write=function(s){//код для отладки:
  document.writeln(s.replace(/</g,"<")
    .replace(/\n/g,"<br><br>"));}  */
document.body.onclick=function(ev){
  lolo(document.all?event:ev);}
table=['Главный чекбокс первой таблицы и формы (заголовок'
  +' уровня 1)',['привет 1!','Заголовок 2!',['привет 2.1!'
  ,'Заголовок 2.2!',['привет 2.2.1!','привет 2.2.2!']
  ,'привет 2.4!'],'привет 3!'],['привет 21! (продолжение'
  +' за линией раздела)','привет 22!','привет 23!'
  ,'Заголовок 24!',['hello21','hello22']]];
table2=['Вторая таблица (без подсветки обрамления)'
  ,['hello1','hello2',['hello21']
  ,['hello22, линия раздела другого стиля']]];
var x=0;
function color_it(i,ch){  checkboxes[i].checked=ch;
divs[i].className=divs[i].className
  .replace(divs[i].className.charAt(0)
    ,checkboxes[i].checked?'b':'a');
}
function lolo(event){el=event.srcElement||event.target;
  if((elg=el.getAttribute('g'))){j=elg*1;
    if(checkboxes[j+1]&&checkboxes[j+1].getAttribute(
      'level')>checkboxes[j].getAttribute('level')){
      j1=j+1;j2=0;
      while(checkboxes[j1]&&checkboxes[j1].getAttribute(
        'level')>checkboxes[j].getAttribute('level')){
      j2+=checkboxes[j1].checked*1;j1++;}
      jcount=j1-j-1;
      ch=!(j2==jcount);
      for(x=j;x<j+jcount+1;x++)color_it(x,ch);
    }else color_it(j,!checkboxes[j].checked);
//Пробегаем по всей ветке и ищем чекб.,кот.нужно убрать
    prev=checkboxes[j].getAttribute('level');
    while(prev!=0){  prev--;j1=j;
      while(checkboxes[j1]&&checkboxes[j1].getAttribute(
        'level')!=prev)j1--;
      j2=j1;all_c=1;all_u=1;
      while(checkboxes[j2+1] && checkboxes[j2+1]
        .getAttribute('level')!=prev){  j2++;
        if(checkboxes[j2].checked) all_u=0;
      else all_c=0;}
      sel=!all_u;
      color_it(j1,sel);
    }
}}
function gt(a,e,table_style){  t='';
  for(i in a){  if(typeof(a[i])=='string'){
    x++;
    omo="this.className=this.className.replace(this.clas"
+"sName.charAt(1),this.className.charAt(1)=='n'?'o':'n')";
    t+='<tr><td><div onmouseover="'+omo+'" onmouseout="'
      +omo+'" class="an'+x%2+''+e+'" g="'+x+'" style="'
      +(self.first_row?'border-top:'+table_style+'px '
      +(table_style>2?'solid':'dashed')+' #884488':'')
      +';margin-left:'+10*e+'" level='+e
      +'><img src=../images/favicon3.gif onclick="if(hid'
      +x+')(hid'+x+'.style.display=hid'+x
      +'.style.display==\'\'?\'none\':\'\')"> <input type='
      +'checkbox onclick=this.checked=!this.checked level='
      +e+' g="'+x+'">'+a[i]+'</div></td></tr>\n';
    first_row=0;
  }else{if(a[i-1]&&typeof(a[i-1])!='string')first_row=1;t+=
  '<tr><td><table cellspacing=0 cellpadding=0 width=100%">'
      +gt(a[i],e+1,table_style)+'</table></td></tr>\n';
  }}return t;
}
document.write('<form><table cellspacing=0 cellpadding=0'
  +' width=90%><div class="an'+(x%2)+'0" g="0" level=0>'
  +'<input type=checkbox onclick="this.checked=!this.c'
  +'hecked" level=0 g="0">Выделить все чекбоксы</div></td>'
  +'</tr></table>\n<table cellspacing=0 cellpadding=0'
  +' width=90%>'+gt(table,1,2)+'</table></form>\n\n'
  +'<div g='+(x+1)+' level=0>\n');
document.write('2-я таблица в стиле 2:<table cellspacing=0'
  +' cellpadding=0 width=90%><form>'+gt(table2,1,1)
  +'</form></table>');
document.write('2-я таблица в стиле 3:\n\n<table cellspaci'
  +'ng=0 cellpadding=0 width=90%><form><div g='+(x+1)
  +' level=0>\n'+gt(table2,1,3)+'</div></form></table>');
var checkboxes=[];var divs=[];var lvl=[];
for(i0=0;i0<document.forms.length;i0++)
  for(i=0;i<(df=document.forms[i0]).length;i++)
    if(df[i].getAttribute('g'))
      checkboxes[df[i].getAttribute('g')]=df[i];
for(i=0;i<(df=document.getElementsByTagName('DIV')).length;
  i++)
  if((i1=df[i].getAttribute('g'))){
    lvl[i1]=df[i].getAttribute('level');
    divs[i1]=df[i];
    color_it(df[i].getAttribute('g'),checkboxes[df[i]
      .getAttribute('g')].checked)
}
</script>

qt() - функция генерации таблицы. Её следует менять для получения других результатов генерации. Контролировать получающийся html-код - тем же методом, что и в прежнем примере, раскомментировав document.write=function(s)... .

Расцветки разных уровней структуры, которых может быть сколько угодно, задаются в стилях. К примеру, 4-й уровень (подсветка) выделен ярко-оранжевым.

Условие вставки линии-разделителя между строками - должно быть 2 соседних массива в массиве table, в котором задано содержание таблицы. Посмотрев в код, легко увидеть закономерности.

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

Таблица имеет особенность, позволяющую размещать её активные компоненты (записи в строках) вне таблицы или в отдельных колонках таблицы. В примере это не продемонстрировано, но внимательный просмотр кода показывает, что работают-то не строки, как в прежних примерах, а блоки (div), лежащие в ячейках. Значит, их можно поместить и без таблицы в виде списка. Или оставить таблицу, а в соседней её колонке поместить другой многоуровневый список. Или перенести всю активность из тегов div в теги tr, и получим работу списка по всей строке, как в прежних примерах. Всё это требует, конечно, презаписи функции qt() в подходящем виде.

 

Демонстрация работы скриптов находится на странице примеров. Напомню, что основой написания статьи послужило обсуждение вопроса создания таблиц с подсветкой в форуме. Возможно, мы вернёмся в будущих статьях к построению таблиц, отражающих структуру данных. Всё зависит от активности обсуждения результатов и заинтересованности читателей.

Уровень: для программистов

Авторы, 12345с, codexomega,
Ve-ve
-v---

Новости свйта.
11.06.06

Новый сервис сайта: "Вопрос-ответ"

Оперативные ответы на вопросы по Javascript, применению стилей, тегов HTML, языков PHP, Perl для работы с сервером.

Уважаемые подписчики, приглашаю Вас пользоваться новым сервисом сайта - форумом для вопросов и ответов по Javascript. В нём вы получите возможность оперативно прочитать ответ на вопрос. Время ответа - от 2-4 частов до 1 дня. Подобной практикой общения с разработчиками веб-страниц я занимаюсь на 2-3 форумах: softtime.ru (ник 12345), vingrad.ru (ник 12345с), rsdn.ru (ник 12345_). Она помогает быть в курсе типичных вопросов и проблем - от простых до сложных. Открытие раздела вопросов-ответов на сайте javascript.aho.ru позволит оперативно отслеживать настроения читателей рассылки, писать статьи на темы актуальных вопросов, точнее адресовать статьи в рассылке, пополнять запас решений на сайте.

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

Уровень: для всех
-v---

 javascript.aho.ru , © I.Svetlov, 2005-2006 
Текущая очерёдность плана статей (подписчики могут корректировать через голосование).
9. Таблицы с подсветкой строк и выделением по клику. Подсветка и выделение части строки.
8. Ключевые слова новых технологий, которые нужно знать разработчику веб-страниц.
3. Как писать тексты с доступом через JS без экранирования специальных символов (&lt; и другие).
4. select и list - в них есть много общего. Как и с меню навигации. Эмулятор селекта.
5. Древовидное меню, подход к данным, отделение данных от представления.
6. Многонедельный календарь со ссылками. (По списку строится календарь.)

Если обнаружилось, что приходили не все письма, пожалуйста, сообщите об этом автору рассылки, чтобы помочь разобраться, какие коды некоторые почтовые серверы не пропускают. Предложения о темах, вопросы по Javascript, замечания по рассылке сообщайте на форуме или отправляйте на почту автора.

 


В избранное