Autor Zpráva
joe
Profil
Není to tak dlouho, co jsem se tu ptal na strukturu externích souborů v JavaScriptu. Od té doby mi prošlo pod rukama hodně kódu, který je většinou dost dlouhý, nečitelný a vůbec nezformátovaný. S logikou na tom není o moc lépe, ale kupodivu funguje. Vzhledem k tomu, že poslední dobou pracuji s jQuery (usnadní mi hodně práce a psaní :-)) a vídám v souborech něco takového

if($("#el").length) {
  $("#el").click(function () {
    $("#el .cls").fadeOut("normal", function () {
      $("#el").addClass("ok");
    });
  });
}
předpokládám, že správně by mělo být
var $el = $("#el");
if ($el.length) {
  $el.click(function () {
    $el.find(".cls").fadeOut("normal", function () {
      $el.addClass("ok");
    });
  });
}

V hodně dlouhém a takto zapsaném JS souboru se pak málokdo vyzná a argument, že se dá použít Ctrl+F se mi teda vůbec nelíbí, kromě toho může způsobovat problémy, kdy na různých stránkách jsou různé elementy se stejným identifikátorem.

Přemýšlel jsem nad tím, jak psát kód tak, aby:

1. bylo hned jasné, jaký kód se bude volat
2. nedocházelo k nechtěnému volání kódu při použití stejného názvu selektoru
3. byl přehledný a v malých funkcích
...

Nerad bych vymýšlel něco, co třeba už existuje :-) a tak mne napadlo, že bych kód psal s využitím atributů (např. datových v HTML5) a zase si trochu ušetřil práci. Dám příklad, kód v HTML:

<div data-namespace="shop.product">
  <div class="product">
    <a href="#" data-call="addToCart">Přidat do košíku</a>
  </div>
</div>

s tím, že při kliku na odkaz by vyvolalo funkci shop.product.addToCart()

Má to své mouchy a nevím jestli jsem teď zrovna vybral správný příklad, ale zdá se vám takové řešení lepší než podmínkování, jestli daný element existuje a na základě toho zavolej nějakou akci? A nebo pokud znáte nějaký fw na něco podobného, co mě napadlo, rád bych se na to podíval... díky :-)
Chamurappi
Profil
Reaguji na joa:
Není to trochu krkolomná cesta k tomu, co se normálně dělá atributem onclick?
Chápu, že občas se hodí vynést logiku událostí z HTML do externího skriptu, ale tobě se to zjevně nehodí a tak hledáš zase způsob, jak ji našroubovat dovnitř… proč tak složitě?
peta
Profil
Ten jquery, cos poslal je urcite ukazkovy. Verim, ze ten, kdo si to bude zabudovavat do stranky pojmenuje class a id delsimi nazvy. Mi ten jquery prijde casto prehlednejsi nez bezny js. Ale tak nikdo te nenuti ho pouzivat.

Mam pocit, ze se to pise jeste trochu jinak, nez jsem ti to upravit. Ten $ je prave pojmenovani funkce, protoze to JS bohuzel umoznuje, aby se funkce jmenovala $. Ja bych tam pouzil misto id proste class, treba ".addToCart".
var el = $("#el");
if (el.length) {
  el.click(function () {
    el.find(".cls").fadeOut("normal", function () {
      el.addClass("ok");
    });
  });
}

Pro formatovani kodu existuji programy. Treba pspad pouzivam casto jen kvuli formatovani. Neni to nic moc, ale lepsi nez mit kod na jednom radku.
joe
Profil
Chamurappi:
Možná ano :-) proto se ptám.

Celou logiku nechat v JS mi přijde hrozně nepřehledný a může vést ke kolizím právě kvůli těm názvům, zvlášť pokud jsou obecné. Navíc na první pohled do kódu není vidět, co se bude dít. Je paráda upravovat JS soubory, co mají na začátku definování události "document ready" a na řádku 2000 končící závorku...

Osobně jsem se naučil použití "namespace", asi i v JS se tomu tak dá říkat, a kód si tak člením do logických prostorů a co možná nejkratších funkcí. To mě právě vedlo k řešení problému, kdy takové funkce volat. U toho odkazu to samozřejmě moc nemá smysl a bylo by lepší zvolit onclick, ale řekněmě, že chceš z nějakého elementu vytvořit třeba Google mapu (s využitím API), budeš mít funkci web.map.create a teď, kdy ji zavoláš?

Nebyl by jednodušší třeba takový zápis

<div id="google-map" data-namespace="web.map"> <!-- určím si, z jakého namespace se budou volat funkce pokud nebudou volány přímo -->
<div class="container" data-call="create"></div> <!-- zavolá funkci web.map.create() například s parametrem tohoto elementu, takže tu mapu vytvoří do něj -->
<div class="controls" data-call="web.map.controls"></div> <!-- volá funkci přímo, nemá vliv na nejbližší definovaný jmenný prostor (i když je tady stejný) -->
</div>

Nemůžu teď přijít na důvod, proč jsem něco takového vymýšlel :-) a možná to je celé nesmysl, ale hledal jsem globální řešení, tím že bych si každý element takto nějak (data-call) označil, hned bych viděl co se s ním bude v JS dělat.

Šlo mi o to, abych zautomatizoval tento proces a úplně se mu vyhnul:

var $el1 = $("#el1");
if ($el1.length) {
  // tady něco udělej
}

var $el2 = $("#el2");
if ($el2.length) {
  // tady něco udělej
}

// ... je to pořád dokola

peta:
aby se funkce jmenovala $
$ je přece název funkce a jinak obecně platí taková konvence, že všechno co je jQuery objekt, má před názvem právě $.
Chamurappi
Profil
Reaguji na joa:
Je paráda upravovat JS soubory, co mají na začátku definování události "document ready" a na řádku 2000 končící závorku...
Dost často takové zrůdnosti páchají lidé, kteří si neuvědomují, že v JavaScriptu můžou používat vlastní funkce a vlastní proměnné. Vidí všude jen callbacky a tečky, tak jim přijde přirozené a správné volat desetkrát totéž v jedné dlouhé špagetě…

Osobně jsem se naučil použití "namespace", asi i v JS se tomu tak dá říkat
Dělám to podobně. Jinak bych se z toho zbláznil.

budeš mít funkci web.map.create a teď, kdy ji zavoláš?
Za elementem, do kterého mapa patří. Plácnu tam <script> a hotovo. Nevidím žádnou výhodu deklarativního zápisu.
joe
Profil
Chamurappi:
Plácnu tam <script> a hotovo
Najdou se tací, co nesnesou <script> v HTML pro nic jiného, než k nalinkování externího souboru. Nepatřím k nim :-)

Jinak to tak možné a vhodné samozřejmě je, ale jak bys takovým způsobem řešil přizpůsobení mapy? Tzn. různá nastavení apod. Odpovím si sám, že přes objekt jako jeden z parametrů v metodě :-) jen by mi to přes ty parametry přišlo takové cool...
ShiraNai7
Profil
joe:
Najdou se tací, co nesnesou <script> v HTML pro nic jiného, než k nalinkování externího souboru. Nepatřím k nim :-)

Já třeba ano. Nemyslím si, že by se měly scripty jen tak plácat do HTML. Jakékoliv dodatečné parametry lze definovat třídami či data- atributy (nemyslím teda způsobem, jaký uvádí joe - nějaké callbacky nebo co). Ale prakticky to špatně není a je to jen otázka vkusu :)
Str4wberry
Profil
Ale prakticky to špatně není a je to jen otázka vkusu :)

Nejen špatné to není, někdy je to v podstatě nezbytné.
Chamurappi
Profil
Reaguji na ShiraNaiho7:
Nemyslím si, že by se měly scripty jen tak plácat do HTML.
Vkus by se měl přizpůsobit efektivitě. Najít zalíbení v ošklivém a jednoduchém je v důsledku užitečnější, než ohýbat funkčnost podle složité umělé představy o kráse.

Někdy pomáhá pěkný kód srozumitelnosti a usnadňuje další vývoj, ale tady bych řekl, že moc ne. Respektive napomáhal by vývoji směrem, který nikdo nepotřebuje (snadnějšímu strojovému zpracování příkazů něčím jiným, než je interpretr JavaScriptu). Vyčlenění skriptů do jedné dlouhé chaoticky poskládané špagety příkazů, která se podle příležitostí přilepuje k jednoúčelovým fragmentům v HTML, také ničemu nepomáhá.
joe
Profil
Chamurappi:
Myslím, že bych nebyl jediný, kdo vymýšlí jednodušší a příjemnější zápis - a to nemluvím jen o JavaScriptu. Tak když bych to měl shrnout, jsou (asi jen) tři možnosti, ze kterých bych si měl vybrat

1. možnost
<div id="photos-carousel" data-call="web.ui.carousel">...</div>

2. možnost
<div id="photos-carousel">...</div>
<script>web.ui.carousel("#photos-carousel");</script>

3. možnost
<div id="photos-carousel"><.../div> a v externím souboru pak
web.ui.carousel("#photos-carousel"); // ale není to ideální, protože nemám přehled na jaké HTML stránce se to bude dít

---

1 - no není to čisté, jednoduché a jasné? Nedocházi k duplicitě selektoru (když se rozhodnu změnit id, nesmím zapomenout ho změnit)
2 - ujde, ale někdo se chce vyvarovat (i když kolikrát zbytečně) používání tagu script (s JS kódem uvnitř) v HTML
3 - není ideální, víme proč

---

Nemyslím, že to co jsem tady navrhnul je super řešení, chtělo by to hezky promyslet, ale podle mě to vypadá na zajímavou cestu, jak si zjednodušit takové JS volání a patlání se v JS kódu


ShiraNai7:
Nemyslím si, že by se měly scripty jen tak plácat do HTML
A internacionalizaci bys pro JavaScript řešil třeba jak, kdybys nechtěl mít v HTML jen tak plácající se skripty?

Str4wberry:
Nejen špatné to není, někdy je to v podstatě nezbytné.
Zacházíme mimo téma, ale můžeš prosím uvést příklad, ať si to dokážu lépe představit? Díky
Chamurappi
Profil
Reaguji na joa:
1 - no není to čisté, jednoduché a jasné?
Má to určitý půvab. Ale tomu, kdo se v tvém díle vyzná, to práci nijak znatelně neusnadní, a všichni ostatní můžou přecenit kouzelnost deklarativního zápisu. Je to atribut a hodně lidí si myslí, že atributy jsou živým zrcadlem činností souvisejících s elementem. Co se stane, když skriptem ten atribut přidají? Když ho odeberou? Když změní hodnotu na jinou funkci? Když si AJAXem natáhnou HTML s tímhle atributem? Nebo když přesunou element s data-call do elementu s jiným data-namespace? Schováš-li pár věcí pod pokličku, neseš odpovědnost za celý hrnec :-)

Viděl jsi někdy zdroják webu postaveného na jQuery Mobile? Autoři tohoto UI frameworku dotáhli tvůj nápad do extrému a půvab se vytratil, vypadá to jako zvrácená mutace HTML.

Nedocházi k duplicitě selektoru
Tomu se jde i ve druhé možnosti vyhnout. Právě vykonávaný <script> je v danou chvíli posledním elementem v DOMu a ze znalosti jeho pozice jde dohledat související element.
Str4wberry
Profil
Zacházíme mimo téma, ale můžeš prosím uvést příklad, ať si to dokážu lépe představit? Díky

Např. pokud se má něco zobrazovat se zapnutým/vypnutým JS, je potřeba to řešit hned, jinak může stránka problikávat. Pokud má mít nějaké formulářové pole po načtení focus, je také vhodné mu ho dát co nejdříve.
joe
Profil
Chamurappi:
Má to určitý půvab. Ale ...
Máš pravdu, ty situace jsem si ani neuvědomoval, ale myslím, že by je z důvodu jednoduchosti ani nebylo třeba nutné řešit - zavedlo by se, že se to provede jen jednou po načtení DOM, případně jindy, pokud se zavolá příslušná funkce, tzn. že by nad kódem byla jednoduchá kontrola.

Právě vykonávaný <script> je v danou chvíli posledním elementem v DOMu a ze znalosti jeho pozice jde dohledat související element.
Dohledat by to šlo, ale nikde není jasně vidět, se kterým elementem to přesně pracuje, př.:

<div id="items"></div>
<script>
// najdu element nad tímto tagem <script>, aktuálně <div id="items">
</script>
Lehce se ale může stát, že ten hlavní element potřebuju obalit
<div class="wrapper"><div id="items"></div></div>
Na první pohled nic divného, ale dostanu se do stavu, kde není vidět chyba, která tam přece je. Očekávaný element se změnil, kromě toho, že pokud bych tento způsob použil na stránce vícekrát, předpokládám, že bude méně efektivní, protože kolikrát je, tolikrát bych použil funkci getElementsByTagName k určení pozice tagu <script>.
Někde jsem už dřív četl, že hledání podle atributu je taky pomalé, takže asi ani tato volba by nebyla nejlepší, ale... nezkoušel jsem. Výhodou těch datových atributů by bylo, že bych si mohl jednoduše třeba říct, jestli se má zvolená funkce provést při DOM ready nebo třeba až po kompletním načtení stránky, dále pak jednoduchá rozšiřitelnost apod.

Str4wberry:
S tím souhlasím :-)
preca1
Profil
Zdravim,
joe
moc nechápu, co řešíš.
Pokud chceš něco udělat (zavolat funkci), tak to děláš na základě nějaké události.
Pokud se nevyznáš v kódu, tak se nauč, jak ho psát líp (snažil sem se naznačit níže).
Pokud ti kolidujou identifikátory, tak vymýšlej lepší.

...řekněmě, že chceš z nějakého elementu vytvořit třeba Google mapu (s využitím API), budeš mít funkci web.map.create a teď, kdy ji zavoláš?
Tehdy, když tu mapu chceš vytvořit, ne?

Jak psát kód tak, aby:
1. bylo hned jasné, jaký kód se bude volat,
2. nedocházelo k nechtěnému volání kódu při použití stejného názvu selektoru,
3. byl přehledný a v malých funkcích.


– Jmenné prostory, logické ID a CSS třídy, hezky strukturovaný kód, posluchače událostí.
– Pro každou stránku vlastní jmenný podprostor (vlastní modul - nemusí se používat YUI) a vlastní JS soubor.
– Privátní proměnné definuju na začátku souboru, pro veřejné mám gettery a settery.
– Každý modul (pokud je to potřeba a neslouží třeba jen jako "statická třída") má veřejnou init metodu, která ten modul připraví (přidá obsluhy událostí, vygenereuje potřebné HTML elementy, atd).
– Na všechny elementy, se kterejma chci pracovat, navěsím obsluhu ve funkci subscribeForEvents, kterou volám v init metodě onoho modulu, takže přesně vím, který elementy jsou ovládacími prvky té stránky a přesně vím, jaká metoda se pro každou událost volá. Tuto metodu mám v JS souboru vždy jako první.
– Pokud chci pracovat s hodnotama, který mi přijdou ze serveru, tak je vkládám jako parametr init funkce, nebo si na to vytvořím veřejnou metodu.
– Init metodu pak stačí zavolat na onload nebo v posledním <script>u v body.

V HTML pak mám jen 2 elementy <script> - jeden na vložení souboru, druhej na zavolání init metody. Nic nebrání tomu, mít na jedný stránce víc modulů, který spolu mohou spolupracovat (ať už pomocí eventů (klidně i mnou specifikovaných), nebo volání veřejných metod).

Tenhle postup sem se naučil v práci a nikdy sem neměl problém číst kód svůj něbo někoho jiného. A kupříkladu ExtJS používají stejný princip.
joe
Profil
preca1:
V práci pravděpodobně programujete aplikace (viz třeba ten ExtJS), nikoli klasické webové stránky, jestli jsem to z toho pochopil. Taky to může skončit tak, že webová prezentace má přes tisíc stránek a na každé chceš trošku něco jiného a určitě nevidím ideálnost v tom, abych měl tisíc JavaScriptových souborů :-)

Tehdy, když tu mapu chceš vytvořit, ne?
Tj. kdy? Můžeš volání dát hned pod element pro mapu (některé redakční systémy ti však nemusí dovolit psaní skriptů), můžeš ji volat v době, kdy je načtený DOM a nebo třeba celá stránka.

Ten postup zní celkem dobře, ale v případě že bude modul určen pro jednu stránku, dejme tomu "stránka s detailem" a modul "detail" a teď budu chtít i na jiné stránce využít to samé, co je ve stránce s detailem, natáhnu modul "detail"? V případě takových různých kombinací vznikne docela zmatek, protože pokud na stránce "o nás" budu načítat modul "detail", nezdá se mi to nejlepší řešení.

Kdybys měl, rád bych se podíval na nějaký příklad.

---

Během různého zkoušení se mi objevily i problémy s prohlížeči.
V souboru, který jsem načítal v hlavičce, jsem měl var App = {}; App.run = function () {...};
před </body> pak volání App.run(); v Internet Exploreru jsem dostával chybu, že App je undefined

---

Ve Firefoxu jsem měl kód
function a () {
    b();
}

function b () {

}
a při zavolání funkce a() hlásil, že b není funkce (nikde žádné stejné jméno nebylo, po přehození funkce b) nad a vše fungovalo)
_es
Profil
joe:
Ve Firefoxu jsem měl kód...
Nebola by „živá“ ukážka toho kódu? Ak sú definície oboch funkcií v jednom elemente script, tak nezáleží na poradí definícií. Ak boli v rôznych scriptoch, tak to nastať mohlo.
preca1
Profil
joe:
Promiň za delší odezvu.

V práci pravděpodobně programujete aplikace...
Tenhle princip používáme i na normální webovce (www intrade com/v4/home/). Většinou každá stránka má unikátní JS funkcionalitu, takže většinu kódu znovu nepoužíváme. Výjimka je třeba http://d1m4lpsf3lzgpi.cloudfront.net/v4/assets/js/contract-list-creator.js?modified=1353327349000&expfilter_setExpiry=true.

Tj. kdy?
To asi záleží na tobě a tvém kódu :).

Ten postup zní celkem dobře, ale...
Jak to navrhnout je podle mě individuální. Pokud budeš mít hodně podobných stránek, tak budeš psát obecný komponenty, který budou znovupoužitelný a třeba využiješ dědění. Pokud budeš mít relativně málo stránek, kde každá bude vyžadovat úplně jinou funkcionalitu (jako my), tak budeš psát konkrétní "třídy", který nebudou znovupoužitelný.

Vaše odpověď

Mohlo by se hodit

Neumíte-li správně určit příčinu chyby, vkládejte odkazy na živé ukázky.
Užíváte-li nějakou cizí knihovnu, ukažte odpovídajícím, kde jste ji vzali.

Užitečné odkazy:

Odkud se sem odkazuje


Prosím používejte diakritiku a interpunkci.

Ochrana proti spamu. Napište prosím číslo dvě-sta čtyřicet-sedm:

0