Spis treści:

Kategoria:HTMLJavaScript


Zaznaczanie komórek, wierszy i kolumn w tabeli HTML

HTML potrafi coraz więcej

Jeszcze nie tak dawno arkusze kalkulacyjne były całkowicie poza zasięgiem aplikacji internetowych. Postęp jest jednak nieunikniony, a same strony stają się coraz bardziej skomplikowane. Tym razem za cel postawiłem sobie stworzenie tabeli, której komórki będą mogły być zaznaczane przez użytkownika myszką. To otwiera szereg zastosowań, w szczególności związanych z obliczeniami i analizą danych - coś na kształt arkusza kalkulacyjnego. Żeby nie przedłużać, przejdźmy do przykładu.

Tabela w działaniu

Użytkownik ma możliwość zaznaczania dowolnych komórek (prostokątne obszary) i realizowania dla nich operacji sumy. Żebym nie musiał tłumaczyć, zapraszam do przetestowania.

1121314151617181
1222324252627282
1323334353637383
1424344454647484
1525354555657585
1626364656667686
1727374757677787
1828384858687888

Suma wartości w zaznaczonych komórkach:

Pełny listing HTML i JavaScript

Uznałem, że całe rozwiązanie zostanie zrealizowane w HTML i JavaScript, bez udziału bibliotek zewnętrznych. Zdarza się, czego nie popieram, że dla kilku lub kilkunastu linijek kodu dołączane są całe bilioteki, zajmujące nieraz kilkadziesiąt kilobajtów. Realizacja tego samego zadania w jQuery byłaby pewnie prostsza, ale czasem warto się wysilić i oszczędzić użytkownikom niepotrzebnego transeru.

<html>
<head>
<title>Zaznaczanie komórek w tabeli HTML</title>
</head>
<body>
<style>
.SimpleTable{border-collapse:collapse;}
.SimpleTable, .SimpleTable td{border: 1px solid black;padding:2px;}
.White{background:#FFFFFF}
.Selected{background:#A0FFA0}
</style>
<table id="MyTableclass="SimpleTable">
<tr><td>11</td><td>21</td><td>31</td><td>41</td><td>51</td><td>61</td><td>71</td><td>81</td></tr>
<tr><td>12</td><td>22</td><td>32</td><td>42</td><td>52</td><td>62</td><td>72</td><td>82</td></tr>
<tr><td>13</td><td>23</td><td>33</td><td>43</td><td>53</td><td>63</td><td>73</td><td>83</td></tr>
<tr><td>14</td><td>24</td><td>34</td><td>44</td><td>54</td><td>64</td><td>74</td><td>84</td></tr>
<tr><td>15</td><td>25</td><td>35</td><td>45</td><td>55</td><td>65</td><td>75</td><td>85</td></tr>
<tr><td>16</td><td>26</td><td>36</td><td>46</td><td>56</td><td>66</td><td>76</td><td>86</td></tr>
<tr><td>17</td><td>27</td><td>37</td><td>47</td><td>57</td><td>67</td><td>77</td><td>87</td></tr>
<tr><td>18</td><td>28</td><td>38</td><td>48</td><td>58</td><td>68</td><td>78</td><td>88</td></tr>
</table>
<p>Suma wartości w zaznaczonych komórkach: <b><span id="Result"/></b></p>
<script>
function Mouse(){
  this.IsDown = false;
}
Mouse.prototype.OnDown = function(cell,mouseEventArgs){
  this.IsDown = true;
  this.UnselectAllCells();
  this.SelectedRow = cell.Row;
  this.SelectedColumn = cell.Column;
  this.SelectCell(cell);
  mouseEventArgs.preventDefault();
}
Mouse.prototype.AttachTableCells = function(tableID){
  this.Table = document.getElementById(tableID);
  for (var r=0; r<this.Table.rows.length; r++)
    for (var c=0; c<this.Table.rows[r].cells.length; c++){
      var cell = this.Table.rows[r].cells[c];
      cell.Row = r;
      cell.Column = c;
      cell.addEventListener("mouseover"this.OnMoveOver.bind(this, cell), false);
      cell.addEventListener("mousedown"this.OnDown.bind(this, cell), false);
    }
  window.addEventListener("mouseup"this.OnUp.bind(this), false);
}
Mouse.prototype.OnMoveOver = function(cell,mouseEventArgs){
  if (this.IsDown){
    var minRow = Math.min(cell.Row, this.SelectedRow);
    var maxRow = Math.max(cell.Row, this.SelectedRow);
    var minCol = Math.min(cell.Column, this.SelectedColumn);
    var maxCol = Math.max(cell.Column, this.SelectedColumn);
    for (var r=0; r<this.Table.rows.length; r++){
      for (var c=0; c<this.Table.rows[r].cells.length; c++){
        var cell = this.Table.rows[r].cells[c];
        if (cell.Row>=minRow && cell.Row<=maxRow &&
            cell.Column>=minCol && cell.Column<=maxCol)
          this.SelectCell(cell);
        else
          this.UnselectCell(cell);
      }
    }
  }
}
Mouse.prototype.UnselectCell = function(cell){
  cell.className = "White";
  cell.IsSelected = false;
}
Mouse.prototype.SelectCell = function(cell){
  cell.className = "Selected";
  cell.IsSelected = true;
}
Mouse.prototype.UnselectAllCells = function(){
  for (var r=0; r<this.Table.rows.length; r++)
    for (var c=0; c<this.Table.rows[r].cells.length; c++)
      this.UnselectCell(this.Table.rows[r].cells[c]);
}
Mouse.prototype.OnUp = function(mouseEventArgs){
  this.IsDown = false;
  var result = document.getElementById("Result");
  var sum = 0;
  for (var r=0; r<this.Table.rows.length; r++)
    for (var c=0; c<this.Table.rows[r].cells.length; c++)
      if (this.Table.rows[r].cells[c].IsSelected)
        sum += parseInt(this.Table.rows[r].cells[c].innerHTML);
  result.innerHTML = sum;
}
window.addEventListener("load"function(){
  var mouse = new Mouse();
  mouse.AttachTableCells("MyTable");
  }, false);
</script>
</body>
</html>

Proces inicjalizacyjny

Cała zabawa zaczyna się po załadowaniu strony (zdarzenie load obiektu window). To tam tworzony jest obiekt Mouse, w którym umieszczone są wszystkie potrzebne metody. Warto zwrócić uwagę na to, że nie ma żadnej zmiennej globalnej - dzięki temu może jednocześnie istnieć kilka takich samych obiektów, a każdy z nich pracuje niezależnie. Ten obiekt Mouse posiada metodę AttachTableCells, która "udoskonala" tabelę. Jednym z tych ulepszeń jest numeracja wierszy i kolumn - takie podejście znacznie upraszcza przyszłą pracę, bo z łatwością możemy określić, z którą komórką mamy do czynienia i gdzie się ona znajduje w strukturze tabeli. Drugim ulepszeniem jest podpięcie zdarzeń:

  • mouseover - Zdarzenie wywoływane jest w momencie najechania myszą na komórkę tabeli, obsługiwane w metodzie OnMoveOver.
  • mousedown - Zdarzenie wciśnięcia przycisku, obsługiwane w metodzie OnDown.

Oprócz tego nasłuchiwane jest zdarzenie zwolnienia przycisku myszy mouseup i obsługiwane w metodzie OnUp. Dzieje się to na poziomie całego okna - to na wypadek, gdybyśmy uciekli kursorem poza obszar tabeli.

Funkcje i zmienne pomocnicze

Zwrócę jeszcze uwagę na kilka detali. Po pierwsze, podczas tworzenia obiektu (wiem, to nie obiekt, ale wygodniej mi go tak traktować) ustawiana jest zmienna IsDown. Określa ona, czy przycisk myszy jest wciśnięty, czy nie. Po drugie, aby nie mieszać kodu zaznaczania i odznaczania komórek, napisałem sobie trzy metody:

  • UnselectCell - Odznacza komórkę tabeli, jako parametr przyjmuje obiekt przetwarzanej komórki.
  • SelectCell - Zaznacza komórkę tabeli, jako parametr przyjmuje obiekt przetwarzanej komórki.
  • UnselectAllCells - Odznacza wszystkie komórki obsługiwanej tabeli.

Cała reszta to już logika właściwa. Przyjrzyjmy się jej dokładniej.

Wciśnij, przeciągnij i zaznacz komórki, zwolnij

Kategoria:HTMLJavaScript

, 2013-12-20

Komentarze:

slim (2016-09-16 10:25:29)
Jak zrobić, żeby po najechaniu myszą na daną komórkę podświetlała się "na żywo" kolumna, bo wiersz wystarczy zwykłym hoverem w css zrobić, ale z kolumną nie jest już tak łatwo.
Kiedyś widziałem skrypt, gdzie podświetlały się wszystkie komórki w wierszu i kolumnie do komórki nad którą była myszka, ale teraz nie mogę tego znaleźć. Równie dobrze mogłyby się podświetlać wszystkie komórki w całym wierszu i całej kolumnie.
PD (2016-09-22 10:05:26)
Powycinałem różne fragmenty bazując na przykładzie. Aby śledzić kolumnę wystarczy:
function Mouse(){}
Mouse.prototype.AttachTableCells = function(tableID){
  this.Table = document.getElementById(tableID);
  for (var r=0; r<this.Table.rows.length; r++)
    for (var c=0; c<this.Table.rows[r].cells.length; c++){
      var cell = this.Table.rows[r].cells[c];
      cell.Row = r;
      cell.Column = c;
      cell.addEventListener("mouseover", this.OnMoveOver.bind(this, cell), false);
    }
}
Mouse.prototype.OnMoveOver = function(cell,mouseEventArgs){
    var col = cell.Column;
    for (var r=0; r<this.Table.rows.length; r++){
      for (var c=0; c<this.Table.rows[r].cells.length; c++){
        var cell = this.Table.rows[r].cells[c];
        if (cell.Column == col)
          this.SelectCell(cell);
        else
          this.UnselectCell(cell);
      }
    }
}
Mouse.prototype.UnselectCell = function(cell){
  cell.className = "White";
  cell.IsSelected = false;
}
Mouse.prototype.SelectCell = function(cell){
  cell.className = "Selected";
  cell.IsSelected = true;
}
window.addEventListener("load", function(){
  var mouse = new Mouse();
  mouse.AttachTableCells("MyTable");
  }, false); 
gil (2017-01-03 11:23:19)
Witam, Czy można przerobić skrypt tak, aby można byłoby zaznaczać tylko te komórki w których dodana jest klasa White np:<td class="White">18</td>?
Dodaj komentarz
Wyślij
Ostatnie komentarze
bardo ciekawe , można dzięki takim wpisom dostrzec wędkę..
Bardzo dziękuję za jasne tłumaczenie z dobrze dobranym przykładem!
Dzieki za te informacje. były kluczowe
Dobrze wyjaśnione, dzięki !