Pár tipů na psaní projektových javascriptů

Javascripty jsou jedna z největších bolestí při vývoji webových stránek. Občas se člověk neudrží a s odstupen času by si radši prostřelil nohu, než aby hledal bug v napsaném skriptu.

Od verze 2.6 je připravená výchozí struktura souboru init-app.js pro projekty. Defaultní podobu najdete v cdn/template_core/js/init-app.js. Sjednocená struktura nám umožní přehledně upravovat vše v jednom souboru.

Základní rozdělení skriptů je popsáno v draftu JS styleguidu. Pro rychlou představu je hierarchie zobrazena na obrázku níže.

Na každé úrovni je přetížena metoda init: function(){...}, která inicializuje veškeré skripty na webu. Soubor init-app.js není povinný, tzn. pokud nejsou v projektu žádné custom skripty, vůbec v repositáři není.

Tip č.1: Rozšiřujte již existující objekt App

Objekt App je globální JS a inicializuje všechny společné javascripty. Proto je dobré ho použít jako výchozí bod i pro rozšiřování custom skriptů na projektu. V template_core/js/init-app.js je jeho základní podoba včetně komentářů, zjednodušeně vypadá asi takto:

(function($, App, window, undefined) { 'use strict';
  var App = window.App = $.extend(true, {}, App, {
    init: function (config) {
      this.config = config;
      this.initCore(config);
      this.initTemplate();
      
      // your code
    }
  });
});

Tip č.3: Oddělujte kód do funkcí

Každá specifická funkce by měla být oddělená, ve chvíli, kdy je JS jedna dlouhá nekončící nudle, se zpětně velmi těžko cokoli dohledává. Proto chceme funcionalitu separovat a izolovat. Je to krok k více modulárnímu kódu i na úrovni projektu.

Příklad klasického kódu

$(document).ready(function() {
  $(".js-play-video").fancybox({
    type:"iframe",
    fitToView:true,
    width:"80%",
    height:"90%",
    autoSize:false,
    closeClick:false,
    openEffect:"fade",
    closeEffect:"fade",
    helpers : {
      overlay : {
        css : {
          'background' : 'black'
        }
      }
    }
  });

Příklad oddělení jednotlivých funkcí

/*
 * Initialize only project specific functions
 */
initProject: function (){
  var that = this;
  
  $(document).ready(function() {
    that.runPlayVideoLinks();
  });
},

// Function opens link with .js-play-video class in fancybox
runPlayVideoLinks: function (){
  $(".js-play-video").fancybox({
    type:"iframe",
    fitToView:true,
    width:"80%",
    height:"90%",
    autoSize:false,
    closeClick:false,
    openEffect:"fade",
    closeEffect:"fade",
    helpers : {
      overlay : {
        css : {
          'background' : 'black'
        }
      }
    }
  });
},

Tip č.3: Definujte event handlery zvlášť

Toto je spíš kosmetická záležitost, ale obvykle je lepší nepsat event handlery jako anonymní funkce. Kód je přehlednější, dá se lépe debuggovat, refaktorovat a rozšiřovat. Lze pak třeba i event listener odebrat přes funkci off().

Příklad klasického kódu

var $table_trigger = $('.js-table-trigger');
  $table_trigger.click(function (e) {
    e.preventDefault();
    $(this).toggleClass("arrow-open");
    $(this).closest("tr").next().toggle();
  });

Příklad handleru jako samostatné funkce

var $table_trigger = $('.js-table-trigger');
  var handleTableTriggerClick = function(e){
  	e.preventDefault();
    $(this).toggleClass("arrow-open");
    $(this).parent().toggle();
  }
  
  // Handle table trigger collapsing
  $table_trigger.on('click', handleTableTriggerClick);

Tip č.4: Nepoužívejte show() a hide()

Připravené funkce od jQuery jsou pomalé. Obecně je za good practice považováno, pokud chcete nějaký prvek schovat nebo zobrazit, používat třídy. V solidpixels na to máme třídy:

  • show - zobrazí prvek jako display:block
  • hide - schová prvek (s !important)

Tip č.5: Ošetřete výkonově problematické eventy

V JS máme typicky několik problematických eventů, které mohou způsobit problémy, když se špatně použijí. Jsou to hlavně: scroll a resize. Tyto eventy by měly být na webu vždy ošetřené. Slouží k tomu funkce $.throttle a $.debounce, které máme v core utilities. Jejich podrobný popis naleznete v článku Debouncing and Throttling Explained Through Examples.

Následující funkce se volá při každém resizu, což znamená změna o 1px. Pokud okno táhnu o 350px, tak se zavolá 350krát.

$(window).on('resize', handleResize);

Ošetřená varianta se zavolá maximálně jednou za 50ms, což je z hlediska uživatele málo postřehnutelné, ale pro výkon browseru to má velký dopad.

$(window).on('resize', $.throttle(handleResize, 50));