Praktyczne wprowadzenie do JavaScript #12

Dwunasty odcinek kursu pokaże, jak dodawać dynamicznie nowe elementy do formularza. Będzie miło, przyjemnie i – miejmy nadzieje – ciekawie.

Screencast z tego odcinka znajdziecie tutaj, natomiast przykład na żywo tutaj.

Zacznijmy od podstaw problemu. Często bowiem zachodzi potrzeba dodania dynamicznej ilości pól w formularzu. Szczególnie wtedy, kiedy chcemy wgrać wiele plików za jednym razem – w takim wypadku przydaje się button/link, po kliknięciu którego w dokumencie pojawia się nowy <input type=”file” />. Spróbujmy dzisiaj coś takiego zrealizować. Standardowo zaczniemy od gotowego pliku HTML, który nie zawiera ani jednej linijki kodu JavaScript…


<!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" lang="pl-PL">
<head>
<title>Strona z dynamicznym formularzem</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf8" />


<!-- Nasz kod Javascript: -->

<script type="text/javascript">

</script>
</head>
<body>
	
	<div><a href="#add_input" id="add_input">dodaj nowe pole typu file</a></div>

	<form name="add_file" enctype="multipart/form-data" method="post">
		<input type="file" name="file-1" />
		<input type="submit" value="dodaj" />
	</form>
		
	
</body>
</html>

Czas na działanie. Aby dodać nowe pole do formularza, po kliknięciu w link dodaj nowe pole typu file, należy wykonać poniższe kroki:

Czas na wypełnienie naszego planu.

Pierwszy punkt to w zasadzie rutynowe działanie. Musimy nadać właściwość onclick naszemu linkowi po załadowaniu całego dokumentu. Odpowiedzialna za to będzie funkcja Laduj – czyli podobnie, jak w poprzednich odcinkach.


<script type="text/javascript">
	window.onload = Laduj;

	function Laduj()
	{
		document.getElementById('add_input').onclick = DodajElement;
	}
</script>

Oczywiście można by też zastosować poniższy zapis, co wyszłoby na to samo:


<script type="text/javascript">
	window.onload = Laduj;

	function Laduj()
	{
		var link = document.getElementById('add_input');
		link.onclick = DodajElement;
	}
</script>

Po co jednak uprzykszać sobie życie i tworzyć niepotrzebnie długi kod?

Pora teraz zdefiniować nową funkcję, jaka wykona się po kliknięciu naszego linku, a która odpowiedzialna będzie za dodanie nowego elementu do formularza.


	function DodajElement()
	{
	}
document.createElement(nazwa_tagu)

Konstrukcja ta pozwala utworzyć nowy tag o nazwie podanej jako argument.

Na przykład:
var nowy_div = document.createElement(‚div’);

Wypada uzupełnić tę funkcję niezbędnym kodem. Jak zapewnie pamiętacie, powiedzieliśmy sobie, że należy najpierw utworzyć nowy tag (element input) w JavaScript. Pomoże nam w tym celu funkcja .createElement(). Tłumacząc z angielskiego jej nazwę, dochodzimy do niezbyt odkrywczego wniosku, że nie została ona nazwana przypadkowo (createElement = stwórz element). Jako że każdy tag jest dzieckiem całego dokumentu, o czym wspominaliśmy sobie w przeszłości, używamy jej w połączeniu z słowkiem documentdocument.createElement. Stwórzmy więc dzięki niej nowy element input, który zapisany będzie w zmiennej o nazwie element:

	function DodajElement()
	{
		var element = document.createElement('input');
	}

Następnie trzeba nadać naszemu elementowi typ, bowiem każdy tag utworzony przez funkcję createElement jest pozbawiony jakichkolwiek atrybutów, jak name, type, czy też style, jakichkolwiek… Pomoże nam w tym funkcja setAttribute, którą poznaliśmy w 7. odcinku.

	function DodajElement()
	{
		var element = document.createElement('input');
		element.setAttribute('type', 'file');
	}

Dodaliśmy zgodnie z zapowiedziami poniższą linijkę:

element.setAttribute('type', 'file');

Słówko type to nazwa atrybutu, a file to wartość tego atrybutu, to dla przypomnienia.

Pora teraz nadać naszemu nowemu elementowi nazwę, która nie będzie się powtarzać z innymi inputami tego typu. Jako że nowo dodany element będzie którymś z kolei, policzymy dotychczasowe inputy i dodamy do wynikłej sumy 1, na znak, że to następny.

Najpierw zdefiniujmy zmienną o nazwie liczba, która przechowywać będzie liczbę inputów o type=”file”:

	function DodajElement()
	{
		var element = document.createElement('input');
		element.setAttribute('type', 'file');

		var liczba = 0;
		
	}

I teraz pojawiają się małe schody. Otóż nie istnieje żadna funkcja pozwalająca szybko policzyć, ile w danym tagu-rodzicu jest elementów o danym atrybucie. Na przykład ile w divie jest paragrafów z określonym id. Musimy zająć się tym sami i użyć pętli for() {}, którą poznaliśmy w ósmym odcinku. Otóż musi ona się wykonać tyle razy, ile formularz zawiera tagów typu <input>. Jeśli któryś tag będzie miał atrybut type równy file, wtedy zwiększymy wartość zmiennej liczba o jeden na znak, że odnaleźliśmy kolejny element o typie file.

Zanim zaczniemy naszą zabawę z pętlą, warto abyś dowiedział się kolejnej, dość istotnej ciekawostki. Zapewne pamiętasz, jak do pól formularza odnosiliśmy się w ten sposób:

document.forms['nazwa_forma'].nazwa_pola.value

Wiedz, że to nie jedyny sposób. Równie dobrze można użyć właściwości każdego formularza, jakim jest tablica .elements. Wtedy powyższy zapis będzie wyglądał tak:

document.forms['nazwa_forma'].elements['nazwa_pola'].value

Jak widzimy, sprawa jest dość analogiczna do ósmego odcinka, gdzie walczyliśmy z menu. Wtedy również mieliśmy do czynienia z tablicami, które były wynikiem działania funkcji getElementsByTagName(). I również, jak tam, można użyć takiego zapisu przy okazji naszych formularzy:

document.forms['nazwa_forma'].elements[x].value

Gdzie x to liczba, określająca, którym z kolei jest w formularzu interesujący nas element. Analogicznie również można otrzymać liczbę wszystkich pól formularza, odwołując się do właściwości tablic .length:

document.forms['nazwa_forma'].elements.length

Z tej ostatniej właściwości skorzystamy, bowiem nasza pętla for musi wykonywać się tyle razy, ile jest pól w formularzu. Przy każdym wykonaniu pętli sprawdzamy typ danego elementu formularza i dodajemy, bądź nie, jedynkę do zmiennej liczba.

Załóżmy więc, że liczbę wszystkich elementów formularza zapiszemy do zmiennej ilosc:

	function DodajElement()
	{
		var element = document.createElement('input');
		element.setAttribute('type', 'file');

		var liczba = 0;
		var ilosc = document.forms['add_file'].elements.length;
	}

W ten sposób policzyliśmy liczbę pól w formularzu o name=”add_file”. Pora na pętlę for.

	function DodajElement()
	{
		var element = document.createElement('input');
		element.setAttribute('type', 'file');

		var liczba = 0;
		var ilosc = document.forms['add_file'].elements.length;
		for (var i = 0; i < ilosc; i++ )
		{
			if (document.forms['add_file'].elements[i].type == 'file')
			{
				liczba += 1;
			}
		}
		
	}

Począwszy od zmiennej i, równej zero, póki zmienna ta nie jest większa od ilości wszystkich elementów formularza, wykonaj kod w klamrach i dodaj do zmiennej i jeden.

W kod w klamrach jest bardzo prosty. Jak sobie powiedzieliśmy, do konkretnych elementów formularza można się dostać poprzez elements[x], gdzie x to kolejna liczba określająca, który z kolei element jest zapisany w formularzu. Jako że zmienna i to liczba z przedziału od zera do liczby elementów input formularza, podstawiamy ją do .elements i sprawdzamy (if (document.forms['add_file'].elements[i].type == 'file')), czy znaleźliśmy pole o type="file". Jeśli tak, dodajmy jeden do zmiennej liczba, która ilość wszystkich inputów o type="file".

Możemy sprawdzić, czy nasz skrypt działa odpowiednio, podstawiając wartość zmiennej liczba do alert();:

alert(liczba);

Póki nie dodaliśmy żadnego nowego elementu, pętla powinna znaleźć tylko jeden, defaultowy input o type="file". W tym wypadku w okienku, po kliknięciu linka oczywiście, ujrzymy 1.

Jeśli mamy już liczbę wszystkich, interesujących nas elementów, pora na nadanie nazwy naszemu nowemu inputowi.

function DodajElement()
	{
		var element = document.createElement('input');
		element.setAttribute('type', 'file');

		var liczba = 0;
		var ilosc = document.forms['add_file'].elements.length;
		for (var i = 0; i < ilosc; i++ )
		{
			if (document.forms['add_file'].elements[i].type == 'file')
			{
				liczba += 1;
			}
		}
		
		element.setAttribute('name', 'file-'+(liczba+1));
	}

Dodane element.setAttribute('name', 'file-'+(liczba+1)); pozwoli nam nazywać nowe elementy formularza w konwencji form-1, form-2 itd. Wszystko po to, by atrybut name nie powtarzał się. Zwróć uwagę na zapis +(liczba+1) - coś takiego pozwala dopisać do ciągu po lewej sumę w nawiasie. Natomiast zapis bez nawiasów - +liczba+1 - dokleiłby do "file-" odpowiednio wartość zmiennej liczba oraz jedynkę.

Fajnie byłoby też nadać jakiś atrybut style. Jeśli będziemy dodawać nowe inputy, każdy będzie obok siebie, co nie wygląda za dobrze. Nadajmy więc, by uniknąć podobnej sytuacji, display: block każdemu, nowemu elementowi.

	function DodajElement()
	{
		var element = document.createElement('input');
		element.setAttribute('type', 'file');

		var liczba = 0;
		var ilosc = document.forms['add_file'].elements.length;
		for (var i = 0; i < ilosc; i++ )
		{
			if (document.forms['add_file'].elements[i].type == 'file')
			{
				liczba += 1;
			}
		}
		
		element.setAttribute('name', 'file-'+(liczba+1));
		element.style.display = "block";

	}
element.style.display = "block";

Powyższy kod załatwia sprawę w zupełności.

Przyda się również margines:

element.style.margin= "2px";
rodzic.appendChild(dziecko)

Konstrukcja ta dodaje do "rodzica" nowe "dziecko". Rodzicem jest tutaj dany tag HTML w postaci odnośnika doń, dzieckiem zaś nowy tag, który powinien się znaleźć pomiędzy tagami rodzica. Dziecko tworzymy wcześniej przy pomocy metody createElement.

Ostatnim punktem programu jest dodanie nowoutworzonego elementu (który zawiera się w zmiennej element, posiadającej różne, nadane przez nas właściwości, jak np. element.name itd.) do formularza. Odpowie za to funkcja appendChild(). Wychodzi na to, że to formularz będzie rodzicem dla nowego inputa, bo rzeczywiście do niego chcemy dodać nowy element.

	function DodajElement()
	{
		var element = document.createElement('input');
		element.setAttribute('type', 'file');

		var liczba = 0;
		var ilosc = document.forms['add_file'].elements.length;
		for (var i = 0; i < ilosc; i++ )
		{
			if (document.forms['add_file'].elements[i].type == 'file')
			{
				liczba += 1;
			}
		}
		
		element.setAttribute('name', 'file-'+(liczba+1));

		element.style.display = "block";
		element.style.margin= "2px";

		document.forms['add_file'].appendChild(element);

	}
Przykład 1.
var div = document.getElementById('jakis_id');
div.appendChild (document.createElement('p'));

Do diva o id="jakis_id" dodajemy nowoutworzony paragraf.

W ten sposób zapełniliśmy ciało funkcji DodajElement. Ostatnia linijka dodaje utworzone pod zmienną element nowe pole formularza o type="file". W powyższej konstrukcji, odwołujemy się do formularza, jako rodzica, używając document.forms['add_file'].

Nasz dokument wygląda teraz tak:

<!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" lang="pl-PL">
<head>
<title>Strona z dynamicznym formularzem</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf8" />


<!-- Nasz kod Javascript: -->

<script type="text/javascript">
	window.onload = Laduj;

	function Laduj()
	{
		document.getElementById('add_input').onclick = DodajElement;
	}

	function DodajElement()
	{
		var element = document.createElement('input');
		element.setAttribute('type', 'file');

		var liczba = 0;
		var ilosc = document.forms['add_file'].elements.length;
		for (var i = 0; i < ilosc; i++ )
		{
			if (document.forms['add_file'].elements[i].type == 'file')
			{
				liczba += 1;
			}
		}
		
		element.setAttribute('name', 'file-'+(liczba+1));

		element.style.display = "block";
		element.style.margin= "2px";

		document.forms['add_file'].appendChild(element);

	}
</script>
</head>
<body>
	
	<div><a href="#" id="add_input">dodaj nowe pole typu file</a></div>

	<form name="add_file" enctype="multipart/form-data" method="post">
		<input type="file" name="file-1" />
		<input type="submit" value="dodaj" />
	</form>
		
	
</body>
</html>

Kliknijcie w link, by przetestować działanie!

Warto zauważyć, że tym sposobem można dodawać zupełnie inne elementy formularza - choćby textarea, input o type="text" i inne. Wystarczy tylko poeksperymentować z funkcją createElement. O selectach natomiast porozmawiamy już w następnym odcinku.

Komentarze

1

Dzięki, że tym razem pomyślałeś o przykładzie na żywo :-)

2

Nie ma sprawy :-)

3

Mam pytanko, jak dodać do kazdego nowoutworzonego „file” odnosnik „usun” tak aby usunąć niepotrzebnie dodane pola?

Pawel
4

function UsunElement() {

if(document.forms[‚add_file’].lastChild.type == ‚file’)
document.forms[‚add_file’].removeChild(document.forms[‚add_file’].lastChild);

}

jjkk
5

a co z tagami wymagajacymi znacznika zamykajacego?
input nie wymaga zamkniecia, a diva sie otwiera i zamyka?
czy jest jakis parametr w ktorym podajemy informacje czy dany tag jest zamykany czy nie?

Tomek
6

a co z tagami wymagajacymi znacznika zamykajacego?
input nie wymaga zamkniecia, a diva sie otwiera i zamyka?
czy jest jakis parametr w ktorym podajemy informacje czy dany tag jest zamykany czy nie?

To znowu ja ;-)
Dodawanie nowego diva do dokumentu : zajrzyj .

7

Nie musimy za każdym razem liczyć ile już jest inputów. Wystarczy, że utworzymy zmienną globalną (poza ciałem funkcji) z wartością początkową 2, użyjemy jej w funkcji DodajElement do numeracji kolejnych inputów i oczywiście będziemy ją zwiększać o 1 z każdym wywołaniem funkcji. Uzyskamy ten sam efekt, ale prościej i bardziej elegancko. Oczywiście zdaję sobie sprawę z tego, że przykład ma na celu pokazać jak najwięcej z Javascriptu, ale pomyślałem, że może to być cenna uwaga dla kogoś, kto chciałby go użyć na swojej stronie. Dodam, że autor odwalił kawał dobrej roboty z tym kursem, choć jako osoba, która zna już parę języków programowania, muszę czasem przeskakiwać spore fragmenty tłumaczące działanie pętli, zasięg zmiennych itp. ;)

sadi
8

Nie wiem co robię żle, ale ta funkcja usuń
function UsunElement() {

if(document.forms[‚add_file’].lastChild.type == ‘file’)
document.forms[‚add_file’].removeChild(document.forms[‚add_file’].lastChild);

}

nie chce mi działać.

Ja chcę, żeby onmouseover dodawało a onmouseout ten input ma zniknąć.

Nie jestem mocny w js, więc nie wiem co robie źle.

Dodałem

function Laduj()
{
document.getElementById(‚add_input’).onmouseover = DodajElement;
document.getElementById(‚add_input’).onmouseout = UsunElement;
}

to wyskakuje błąd „brak definicji ‚file’.

Zrobiłem tak:

function Laduj()
{
document.getElementById(‚add_input’).onmouseover = DodajElement;
}
{
document.getElementById(‚add_input’).onmouseout = UsunElement;
}
to bledu nie ma, ale nie dziala, pozostaje.

Jeszcze zrobiłem tak:

function Laduj()
{

document.getElementById(‚add_input’).onmouseover = DodajElement; onmouseout = UsunElement;

}

i efekt taki sam, nie ma błędu i nie działa.

help, please!

Dadas
9

Wielkie dzięki za ten artykuł, kawał dobrej roboty;-)

Mick
10

Witajcie,

A dlaczego, kiedy formatuję linijkę kodu z:
element.style.margin = "2px";
na
element.style.margin-top = "2px";, tak jak w css działanie całego skryptu zatrzymuje się?

Dzięki za odpowiedź.

Bloom
11

W JS właściwości stylu nie posiadają myślnika w nazwie, jeśli w css taki występuje to musisz zamiast ‚marign-top’ napisać ‚marginTop’. Tutaj: http://www.w3schools.com/css/css_reference.asp masz wszystkie właściwości css (kliknij na interesujący cię i będziesz miał tam ‚JavaScript syntax:’ z poprawnym użyciem tego w JS.

Bishop
12

Witam
zastanawiam się dlaczego najpierw definiujemy zmienną var element a następnie odnosimy się do niej jako elements i o dziwo to działa.
A tak pozatym to dzięki za super kurs!!!

Adam
13

@Adam
Przez elements nie dobierasz sie do zmiennej zadeklarowanej przez siebie, ale do wlasciwosci pobranej formy document.forms[‚add_file’].elements[i]

goompas
14

Głowiłem się nad tą funkcją i ten komentarz otworzył mi oczy:

Przez elements nie dobierasz sie do zmiennej zadeklarowanej przez siebie, ale do wlasciwosci pobranej formy document.forms[‚add_file’].elements[i]

. Dobry kurs dzięki

dobryPodpis
15

Zamień window.onload na document.DOMContentLoaded

Wiktor
16

Co jeśli dodane pola formularza chciałabym umieścić w konkretnym miejscu?
Mam mniej więcej taki rozkład:
textbox
textbox, który chcę powielić
textbox

powielone textboxy (które chciałabym, żeby znalazły się bezpośrednio pod powielanym textboxem). Jak to rozwiązać? Forma w formie nie mogę zrobić, już to sprawdziłam:)

Kornelya
17

I jeszcze jedno pytanie. Jak z takich wygenerowanych formularzy zczytywać dane do bazy, jeśli np korzystam z textbox’ów?

Pozdrawiam :)

Dodaj komentarz

Dozwolone tagi: <blockquote>, <code>, <strong>