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.
Osztalyneve.prototype.fuggveny = function()
{
     //ide a műveletek
}

É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.

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

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.

<ol>
        <li>
                Első
                <a href="#">Fel</a>
                <a href="#">Le</a>
        </li>
        <li>
                Második
                <a href="#">Fel</a>
                <a href="#">Le</a>
        </li>
</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

<ol>
        <li>
                Első
                <span>
                <a href="#">Fel</a>
                <a href="#">Le</a>
                </span>
        </li>
        <li>
                Második
                <span>
                <a href="#">Fel</a>
                <a href="#">Le</a>
                </span>
        </li>
</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.

<ol class="XLlistItems">
        <li>
                Első
                <span>
                <a href="#" onclick="this.parentNode.parentNode.moveUp(); return false;">Fel</a>
                <a href="#" onclick="this.parentNode.parentNode.moveDown(); return false;">Le</a>
                </span>
        </li>
        <li>
                Második
                <span>
                <a href="#" onclick="this.parentNode.parentNode.moveUp(); return false;">Fel</a>
                <a href="#" onclick="this.parentNode.parentNode.moveDown(); return false;">Le</a>
                </span>
        </li>
</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:

HTMLLIElement.prototype.nextLi = function()
{
        var ul = this.parentNode;
        var nodes = ul.getElementsByTagName('li');

        if (!nodes.length)
        {
                return null;
        }

        if (nodes[nodes.length-1] != this)
        {
                var next = this;
                do
                {
                        next = next.nextSibling;
                }
                while (next.nodeName.toLowerCase() != 'li');
                return next;
        }
        return null;
       
}

HTMLLIElement.prototype.prevLi = function()
{
        var ul = this.parentNode;
        var nodes = ul.getElementsByTagName('li');

        if (!nodes.length)
        {
                return null;
        }

        if (nodes[0] != this)
        {
                var prev = this;
                do
                {
                        prev = prev.previousSibling;
                }
                while (prev.nodeName.toLowerCase() != 'li');
                return prev;
        }
        return null;

}

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.

HTMLLIElement.prototype.moveDown = function()
{
        var ul = this.parentNode;

        var next = this.nextLi();
        if (next != null)
        {
                ul.insertBefore(next,this);
        }

}

HTMLLIElement.prototype.moveUp = function()
{
        var ul = this.parentNode;

        var prev = this.prevLi();
        if (prev != null)
        {
                ul.insertBefore(this,prev);
        }

}

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

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html>
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
        <title>HTML lista újrarendezése javascripttel</title>
                <script type="text/javascript" src="HTMLLIElement.js"></script>
                <link rel="stylesheet" type="text/css" href="css.css" />
    </head>
    <body>
               
        <ol class="XLlistItems">
                <li>
                        Első
                        <span>
                        <a href="#" onclick="this.parentNode.parentNode.moveUp(); return false;">Fel</a>
                        <a href="#" onclick="this.parentNode.parentNode.moveDown(); return false;">Le</a>
                        </span>
                </li>

                <li>
                        Második
                        <span>
                        <a href="#" onclick="this.parentNode.parentNode.moveUp(); return false;">Fel</a>
                        <a href="#" onclick="this.parentNode.parentNode.moveDown(); return false;">Le</a>
                        </span>
                </li>

                <li>
                        Harmadik
                        <span>
                        <a href="#" onclick="this.parentNode.parentNode.moveUp(); return false;">Fel</a>
                        <a href="#" onclick="this.parentNode.parentNode.moveDown(); return false;">Le</a>
                        </span>
                </li>

                <li>
                        Negyedik
                        <span>
                        <a href="#" onclick="this.parentNode.parentNode.moveUp(); return false;">Fel</a>
                        <a href="#" onclick="this.parentNode.parentNode.moveDown(); return false;">Le</a>
                        </span>
                </li>
        </ol>

    </body>
</html>

HTMLLIElement.js

/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

HTMLLIElement.prototype.nextLi = function()
{
        var ul = this.parentNode;
        var nodes = ul.getElementsByTagName('li');

        if (!nodes.length)
        {
                return null;
        }

        if (nodes[nodes.length-1] != this)
        {
                var next = this;
                do
                {
                        next = next.nextSibling;
                }
                while (next.nodeName.toLowerCase() != 'li');
                return next;
        }
        return null;
       
}

HTMLLIElement.prototype.prevLi = function()
{
        var ul = this.parentNode;
        var nodes = ul.getElementsByTagName('li');

        if (!nodes.length)
        {
                return null;
        }

        if (nodes[0] != this)
        {
                var prev = this;
                do
                {
                        prev = prev.previousSibling;
                }
                while (prev.nodeName.toLowerCase() != 'li');
                return prev;
        }
        return null;

}

HTMLLIElement.prototype.moveDown = function()
{
        var ul = this.parentNode;

        var next = this.nextLi();
        if (next != null)
        {
                ul.insertBefore(next,this);
        }

}

HTMLLIElement.prototype.moveUp = function()
{
        var ul = this.parentNode;

        var prev = this.prevLi();
        if (prev != null)
        {
                ul.insertBefore(this,prev);
        }

}

css.css

/*
    Document   : css.css
    Created on : 2010.07.19., 13:05:05
    Author     : rimelek
    Description:
        Purpose of the stylesheet follows.
*/

.XLlistItems
{
        width: 150px;
}

.XLlistItems li
{
        text-align: left;
        position: relative;
        border-bottom: 1px dotted gray;
}

.XLlistItems li span
{
        position: absolute;
        right: 0px;
}

DEMO

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