HTML lista újrarendezése javascripttel

ScreenShot

Szép ez a kép itt bal oldalt. Nemde? Talán nem, de nem is az a lényeg, hanem a funkció. Lehet nem tűnik értelmesnek egy html listában az elemeket össze vissza mozgatni. De ha például ezekben a listákban egy űrlap szövegmezői vannak, amiknek számít a sorrendje ( tömbös megvalósítás ), akkor máris értelmét nyeri. De most a cikkben a mozgatás megvalósítására fogok koncentrálni.

JavaScript DOM manipuláló metódusokkal lehet mozgatni különböző csomópontokat szinte bárhova. De arra, hogy több lista elemet két irányba is mozgathatóvá tegyünk, két lehetőség adódna.

  • Egy függvényt írunk, ami paraméterként kapja meg a mozgatandó elem azonosítóját és a mozgási irányokat.
  • Módosítjuk HTMLLIElement osztályt. Konkrétan felveszünk moveUp() és moveDown() metódusokat a html "li" elemekhez.
  1. Osztalyneve.prototype.fuggveny = function()
  2. {
  3.      //ide a műveletek
  4. }

Én az utóbbit választottam. Előnye, hogy onnantól kezdve bármilyen lista elemet az adott listában könnyedén egy metódushívással eggyel lejjebb, vagy feljebb lehet mozgatni. A megoldásomban csak 1 lépést, de módosítható volna, hogy paraméterként átadható legyen a lépések száma. Ezt már az olvasóra bízom. Többszöri hívással is előidézhető ugyanez.

Csak a megoldás érdekel
Mutasd a demot!

Hogyan is lehet módosítani egy javascript osztályt?
A prototype tulajdonságán keresztül. Amit a prototype tulajdonsághoz hozzáadunk függvényeket, változókat, azokat örökli minden példány. Vagyis tartalmazni fogja.

  1. Osztalyneve.prototype.fuggveny = function()
  2. {
  3.      //ide a műveletek
  4. }

Ugyanígy lehet módosítani a DOM osztályokat is, csak egyre kell figyelni. Opera, Firefox és Chrome böngészőkkel a következő probléma nem áll fenn, de Internet Explorer 8-ban, ha a html dokumentum DOCTYPE -ja nincs megadva, vagy HTML 4 -es van megadva, akkor semmilyen DOM osztályt nem fog tudni JavaScriptből felismerni. Amivel működik:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
Vagy
<!DOCTYPE html>

Na most egy lista valahogy így nézne ki.

  1. <ol>
  2.         <li>
  3.                 Első
  4.                 <a href="#">Fel</a>
  5.                 <a href="#">Le</a>
  6.         </li>
  7.         <li>
  8.                 Második
  9.                 <a href="#">Fel</a>
  10.                 <a href="#">Le</a>
  11.         </li>
  12. </ol>

Na most kizárólag a szebb megjelenés miatt itt a fel, le navigáló linkeket én beteszem egy span-be. Így majd CSS-el jobbra lehet igazítani a két linket külön. Tehát így fog kinézni a lista

  1. <ol>
  2.         <li>
  3.                 Első
  4.                 <span>
  5.                 <a href="#">Fel</a>
  6.                 <a href="#">Le</a>
  7.                 </span>
  8.         </li>
  9.         <li>
  10.                 Második
  11.                 <span>
  12.                 <a href="#">Fel</a>
  13.                 <a href="#">Le</a>
  14.                 </span>
  15.         </li>
  16. </ol>

Na persze ahhoz, hogy bármi is történjen a linkekre kattintva, kell nekik egy onclick eseményt definiálni. És ugye milyen egyszerű volna, hogy ha abban az eseményben semmi más dolgunk nem volna, mint egy függvényt meghívni, ami végzi a dolgát. Mindenféle paraméterátadogatás nélkül. Természetesen a lista elemet el kell érni, így navigálni kell a DOM fában, de az mindegyik eseményben ugyanaz lesz.

Hogyan kell navigálni?
Minden eseményben a this szócska jelöli azt az objektumot, html csomópontot, ahol az esemény definiálva volt.

<a href="#" onclick="this.style.color='red'; return false;">Katt és vörös leszek</a>

Ez nem tesz mást, mint a linkre kattintva megváltoztatja a saját színét vörösre. a return false a végén azért kell, hogy a kattintás hatására ne akarjon elugrani a böngésző.
Minden csomópont ismeri a saját szülőjét is. Szülőnek az számít, amibe közvetlenül tartozik. Példámban ez a span tag-et jelenti. A szülőre a parentNode tulajdonsággal lehet hivatkozni. Mivel itt a li tag még egy szinttel feljebb van, kétszer kell alkalmazni.

this.parentNode.parentNode

Itt kellene a végén meghívni a moveUp() és moveDown() metódusokat. Amik még nem léteznek. De rögtön mutatom azt is. Tehát a lista html része a következő formában fog kinézni most már.

  1. <ol class="XLlistItems">
  2.         <li>
  3.                 Első
  4.                 <span>
  5.                 <a href="#" onclick="this.parentNode.parentNode.moveUp(); return false;">Fel</a>
  6.                 <a href="#" onclick="this.parentNode.parentNode.moveDown(); return false;">Le</a>
  7.                 </span>
  8.         </li>
  9.         <li>
  10.                 Második
  11.                 <span>
  12.                 <a href="#" onclick="this.parentNode.parentNode.moveUp(); return false;">Fel</a>
  13.                 <a href="#" onclick="this.parentNode.parentNode.moveDown(); return false;">Le</a>
  14.                 </span>
  15.         </li>
  16. </ol>

Annyit beszéltem már, jöjjön a JavaScript rész
Először is hozzunk létre egy HTMLLIElement.js nevű fájlt. Abban pedig szükség lesz moveUp és moveDown függvények kifejtésére. Előbb nézzük, miből is áll majd.

Megkeresni a következő (vagy előző) lista elemet (ha van) és a következő után ( vagy előző elé ) be kell tenni az aktuális lista elemet. A keresésre külön metódus is van. nextSibling és previousSibling nevűek. Egyetlen bajuk, hogy a lista elemek közötti üres szóközök és enterek összességében egy "Siblinget" (testvér csomópont) alkotnak. Ami nem biztos, hogy ott van. Ezért célszerűbb a keresésre is írni egy nextLi és egy prevLi metódust.

Ezeknek meg kell nézniük, hogy van-e előző, vagy következő elem. A HTMLLIElement is mindig tudja, ki a szülője, így elérhető maga a lista csomópont. Legyen az rendezett (ol), vagy rendezetlen (ul). Le kell kérdezni belőle az elemeket, és megnézni, hogy az aktuális elem az utolsó, vagy éppen első. Ha nincs előző, akkor a prevLi adjon vissza null értéket. Egyébként magát a következő objektumot. Illetve ha egyáltalán nincs egyetlen elem sem, akkor - bár ilyenkor nincsenek linkek se - akkor is adjon vissza null értéket. Ehhez hasonlóan működik a nextLi is. Lássuk:

  1. HTMLLIElement.prototype.nextLi = function()
  2. {
  3.         var ul = this.parentNode;
  4.         var nodes = ul.getElementsByTagName('li');
  5.  
  6.         if (!nodes.length)
  7.         {
  8.                 return null;
  9.         }
  10.  
  11.         if (nodes[nodes.length-1] != this)
  12.         {
  13.                 var next = this;
  14.                 do
  15.                 {
  16.                         next = next.nextSibling;
  17.                 }
  18.                 while (next.nodeName.toLowerCase() != 'li');
  19.                 return next;
  20.         }
  21.         return null;
  22.        
  23. }
  24.  
  25. HTMLLIElement.prototype.prevLi = function()
  26. {
  27.         var ul = this.parentNode;
  28.         var nodes = ul.getElementsByTagName('li');
  29.  
  30.         if (!nodes.length)
  31.         {
  32.                 return null;
  33.         }
  34.  
  35.         if (nodes[0] != this)
  36.         {
  37.                 var prev = this;
  38.                 do
  39.                 {
  40.                         prev = prev.previousSibling;
  41.                 }
  42.                 while (prev.nodeName.toLowerCase() != 'li');
  43.                 return prev;
  44.         }
  45.         return null;
  46.  
  47. }

Itt látható, hogy ciklusban határozza meg a következő elemet. Erre nem lenne nagy szükség, mivel két "li" között jó esetben maximum white-space karakterek vannak, és együttesen egy elemnek számítanának, de jobb a biztonság. Ezek után már szinte gyerekjáték megírni a moveUp() és moveDown() metódusokat.

  1. HTMLLIElement.prototype.moveDown = function()
  2. {
  3.         var ul = this.parentNode;
  4.  
  5.         var next = this.nextLi();
  6.         if (next != null)
  7.         {
  8.                 ul.insertBefore(next,this);
  9.         }
  10.  
  11. }
  12.  
  13. HTMLLIElement.prototype.moveUp = function()
  14. {
  15.         var ul = this.parentNode;
  16.  
  17.         var prev = this.prevLi();
  18.         if (prev != null)
  19.         {
  20.                 ul.insertBefore(this,prev);
  21.         }
  22.  
  23. }

Az insertBefore metódus egy csomópont elé szúr be egy másik csomópontot. És ha az már el volt helyezve valahol a dokumentumban, akkor onnan kiemeli. Nem másolja. Tehát gyakorlatilag áthelyezésként funkcionál most. Mivel insertAfter metódus JavaScriptben nincs (Osztálykönyvtárak bővítéseit nem számítva), ezért a moveDown() metódus tulajdonképpen a következő elemet helyezi az aktuális elé. A hatás ugyanaz.

A teljes megoldás kompletten:
index.html

  1. <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
  2.     <head>
  3.         <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
  4.         <title>HTML lista újrarendezése javascripttel</title>
  5.                 <script type="text/javascript" src="HTMLLIElement.js"></script>
  6.                 <link rel="stylesheet" type="text/css" href="css.css" />
  7.     </head>
  8.     <body>
  9.                
  10.         <ol class="XLlistItems">
  11.                 <li>
  12.                         Első
  13.                         <span>
  14.                         <a href="#" onclick="this.parentNode.parentNode.moveUp(); return false;">Fel</a>
  15.                         <a href="#" onclick="this.parentNode.parentNode.moveDown(); return false;">Le</a>
  16.                         </span>
  17.                 </li>
  18.  
  19.                 <li>
  20.                         Második
  21.                         <span>
  22.                         <a href="#" onclick="this.parentNode.parentNode.moveUp(); return false;">Fel</a>
  23.                         <a href="#" onclick="this.parentNode.parentNode.moveDown(); return false;">Le</a>
  24.                         </span>
  25.                 </li>
  26.  
  27.                 <li>
  28.                         Harmadik
  29.                         <span>
  30.                         <a href="#" onclick="this.parentNode.parentNode.moveUp(); return false;">Fel</a>
  31.                         <a href="#" onclick="this.parentNode.parentNode.moveDown(); return false;">Le</a>
  32.                         </span>
  33.                 </li>
  34.  
  35.                 <li>
  36.                         Negyedik
  37.                         <span>
  38.                         <a href="#" onclick="this.parentNode.parentNode.moveUp(); return false;">Fel</a>
  39.                         <a href="#" onclick="this.parentNode.parentNode.moveDown(); return false;">Le</a>
  40.                         </span>
  41.                 </li>
  42.         </ol>
  43.  
  44.     </body>
  45. </html>

HTMLLIElement.js

  1. /*
  2.  * To change this template, choose Tools | Templates
  3.  * and open the template in the editor.
  4.  */
  5.  
  6.  
  7.  
  8. HTMLLIElement.prototype.nextLi = function()
  9. {
  10.         var ul = this.parentNode;
  11.         var nodes = ul.getElementsByTagName('li');
  12.  
  13.         if (!nodes.length)
  14.         {
  15.                 return null;
  16.         }
  17.  
  18.         if (nodes[nodes.length-1] != this)
  19.         {
  20.                 var next = this;
  21.                 do
  22.                 {
  23.                         next = next.nextSibling;
  24.                 }
  25.                 while (next.nodeName.toLowerCase() != 'li');
  26.                 return next;
  27.         }
  28.         return null;
  29.        
  30. }
  31.  
  32. HTMLLIElement.prototype.prevLi = function()
  33. {
  34.         var ul = this.parentNode;
  35.         var nodes = ul.getElementsByTagName('li');
  36.  
  37.         if (!nodes.length)
  38.         {
  39.                 return null;
  40.         }
  41.  
  42.         if (nodes[0] != this)
  43.         {
  44.                 var prev = this;
  45.                 do
  46.                 {
  47.                         prev = prev.previousSibling;
  48.                 }
  49.                 while (prev.nodeName.toLowerCase() != 'li');
  50.                 return prev;
  51.         }
  52.         return null;
  53.  
  54. }
  55.  
  56. HTMLLIElement.prototype.moveDown = function()
  57. {
  58.         var ul = this.parentNode;
  59.  
  60.         var next = this.nextLi();
  61.         if (next != null)
  62.         {
  63.                 ul.insertBefore(next,this);
  64.         }
  65.  
  66. }
  67.  
  68. HTMLLIElement.prototype.moveUp = function()
  69. {
  70.         var ul = this.parentNode;
  71.  
  72.         var prev = this.prevLi();
  73.         if (prev != null)
  74.         {
  75.                 ul.insertBefore(this,prev);
  76.         }
  77.  
  78. }

css.css

  1. /*
  2.     Document   : css.css
  3.     Created on : 2010.07.19., 13:05:05
  4.     Author     : rimelek
  5.     Description:
  6.         Purpose of the stylesheet follows.
  7. */
  8.  
  9. .XLlistItems
  10. {
  11.         width: 150px;
  12. }
  13.  
  14. .XLlistItems li
  15. {
  16.         text-align: left;
  17.         position: relative;
  18.         border-bottom: 1px dotted gray;
  19. }
  20.  
  21.  
  22. .XLlistItems li span
  23. {
  24.         position: absolute;
  25.         right: 0px;
  26. }

DEMO

  1. ElsőFel - Le
  2. MásodikFel - Le
  3. HarmadikFel - Le
  4. NegyedikFel - Le
Kategóriák: 
Megosztás/Mentés

Új hozzászólás