« 1 2 »
Autor Zpráva
Witus
Profil *
V mnoha mých scriptech by se mi hodilo, kdyby bylo možné donahrát js soubor za běhu, aby nebylo nutné ho fixně psát přes <script> tag do HTML souboru. Nakonec se mi podařilo vytvořit kód, který mi obstojně běží na všech prohlížečích, kde jsem ho testoval:

function loadscript(address) {
var script = document.createElement('script');
script.type = 'text/javascript';
script.src = address;
// script.addEventListener("load",function () {
// alert("Script se načetl.");
// },true);
document.body.appendChild(script);
}


Nicméně toto je jen polovina toho, co bych od funkce, kterou píšu chtěl. Nyní dojde k načtení scriptu, ale ne hned. Pokud si všimnete zacommentované části - to je to, co bych od scriptu chtěl. Nicméně to nefunguje, <script> tag nepodporuje onload event a document.readyState je sice řešením, nicméně je podporovaný jen IE a Operou. Ja sice samozřejmě možné pomocí window.setInterval kontrolovat, jestli se již script načetl kontrolou obsahu proměnných, případně voláním funkce, která pokud nebude existovat, bude vyhazovat výjimku - nicméně to nabourává použitelnost a flexibilitu scriptu. Nevíte tedy prosím někdo jak zkontrolovat, zda se již element načetl?
Witus
Profil *
Btw vidím, že je tu častoproblém s tím, že lidi nevysvětlí své scripty, takže:

function loadscript(address) {} - snad každý chápe, definice funkce :)
var script = document.createElement('script'); - vytvořím si proměnou s zatím neexistujícím elementem <script>
script.type = 'text/javascript';script.src = address; - přiřadím elementu type atribut a adresu k externímu js souboru
document.body.appendChild(script); - vložím element do těla stránky

script.addEventListener("load",function () {
alert("Script se načetl.");
},true);


Na rozdíl od přidání natvrdo přes script.onload="alert('Script se načetl.');", což nefungovalo vůbec, tohle i něco dělá (zasekne to Internet explorer :D ) a elementu se opravdu onload event přiřadí, nicméně <script> tag, jak už jsem se několikrát přesvědčil, eventy nejspíš nepodporuje. Zatím testuji nahrání scriptu přes:

var temp=window.setInterval("if(document.readyState=='complete'){...kód, který se provede po načtení...window.clearInterval(temp);}",100);


Nicméně document.readyState není příliš podporované a navíc načítám nový script do již načtené stránky, takže i mnohé prohlížeče můžou při takovémto nestandartním počínání nezměnit hodnotu readyState vlastnosti a nechat jí na "complete".
Chamurappi
Profil
Reaguji na Wituse:
Nemůžeš jednoduše zavolat funkci z toho externího JS? Něco jako haloJaUzJsemSeNacetl(); :-)

přidání natvrdo přes script.onload="alert('Script se načetl.');", což nefungovalo vůbec
Takhle se k událostem nepřiřazuje. Když už, tak:
script.onclick = function()
{
  alert("Script se načetl.");
};
Kdykoliv pracuješ s vykonávaným kódem jako s řetězcem, je něco špatně.

var temp=window.setInterval("if(document.readyState
Argumentem setIntervalu též nemusí být řetězec, ale přímo sama funkce.
Witus
Profil *
Díky za podněty. Ano, šlo by zavolat funkci z toho externího js souboru, nicméně to by potom vyžadovalo úpravy těch souborů a tahle loadscript() funkce by ztrácela na efektivitě. Proto by mě zajímalo, jestli je možné otestovat načtení toho scriptu rovnou pomocí té funkce loadscript.
Chamurappi
Profil
Reaguji na Wituse:
Mozilla a Opera podporují na <script>u událost onload.
Explorer a Opera podporují na <script>u událost onreadystatechange, ta se chová víceméně podobně.
Zkus to nějak skloubit.
Witus
Profil *
Ok, díky za nápady, trochu jsem si s tím hrál a protože jsem to nechtěl dělat přes detekci prohlížečů, tak jsem to napsal tak, aby když se spustí jeden event, ať onload, nebo onreadystatechange, tak aby se změnila proměnná z true na false a neprovedla se daná akce 2x. Jako generátor jmen proměnných jsem použil new Date().getTime(), nicméně dostat to dovnitř té definice onload a onstatechange funkce se ukázalo jako oříšek, protože funkce brala proměnnou s časem takovou, jaká byla v době spuštění eventu a ne v době spuštění toho loadscriptu. Nakonec jsem to vyřešil přes eval();

function loadscript(address) {
var time = String(new Date().getTime());
window["loadscript_" + time]=true;
var load = function() {eval("if(window['loadscript_"+time+"']){alert('Script se načetl.');window['loadscript_"+time+"']=false;}");};
var script = document.createElement('script');
script.type = 'text/javascript';
script.src = address;
script.onreadystatechange = load;
script.onload = load;
document.body.appendChild(script);
}
Witus
Profil *
Možná je to trochu neohrabaně napsané, ale už to konečně funguje jak má. Moc děkuju za nápady a pomoc :)
Witus
Profil *
Tak pořád ne. Sice to fungovalo, ale to jen proto, že se funkce spouštěly za sebou a time byl pořád stejný. Pořád nevím, jak to vyřešit.

var load = function() {eval("if(window['loadscript_"+time+"']){alert('Script se načetl.');window['loadscript_"+time+"']=false;}");};

ten time se zjišťuje až ve chvíli spuštění daného eventu. Není to nijak dobrý systém, protože time může být stejný, pokud se spustí funkce několikrát za sebou, už jsem to přepsal tak, že se načítá od nuly proměnná místo používání času, ale stejně potřebuju, aby se ten time definoval už při tom spuštění loadscript. Nějak takhle, kdyby šlo používat řetězce:

var load = "if(window['loadscript_"+id+"']){alert('Script se načetl.');window['loadscript_"+id+"']=false;}";
Witus
Profil *
Asi teď píšu hodně chaoticky, takže to znázorním schématicky:

Spuštění loadscript funkce poprvé - Vytvoří se <script tag> s onload a onreadystatechange když(loadscript_1==true){Proveď akci;loadscript_1=false}
Spuštění loadscript funkce podruhé - Vytvoří se <script tag> s onload a onreadystatechange když(loadscript_2==true){Proveď akci;loadscript_2=false}
Atd. - prostě potřebuji vytvořit při každém spuštění proměnnou s jiným názvem, která bude kontrolovat, aby se nespustilo jak onload, tak onreadystatechange. Problém je v tom, že momentálně to funguje takhle:

Spuštění loadscript funkce poprvé - Vytvoří se <script tag> s onload a onreadystatechange "když(loadscript_"+id+"==true){Proveď akci;loadscript_"+id+"=false;}"

Loadscript se spustí ještě 4x, poté dojde k nahrání scriptů, spustí se onload/ a všech pět onload bude vypadat takhle:
když(loadscript_5==true){Proveď akci;loadscript_5=false}

Což znamená, že se daná akce provede jen jednou a pro script do kterého loadscript funkci zasadím to bude, jako by se načetl jen pátý z načítaných scriptů...
Witus
Profil *
aktuální defektní script:

var loadscript_counter=1;

function loadscript(address,action) {
window["loadscript_"+String(loadscript_counter)]=true;
var load = function() {window.eval("if(window['loadscript_"+String(loadscript_counter)+"']){window.eval("+action+");window['loadscript_"+String(loadscript_counter)+"']=false;}");};

var script = document.createElement('script');
script.type = 'text/javascript';
script.src = address;
script.onreadystatechange = load;
script.onload = load;
document.body.appendChild(script);
loadscript_counter++;
}
_es
Profil
Witus
V tom kóde sa už asi nevyznáš ani sám.
Sprav to tak aby tam nebol eval, window[... a podobné hrôzy.
Chamurappi
Profil
Reaguji na Wituse:
Zbytečně si to komplikuješ. Opakuji: Kdykoliv pracuješ s vykonávaným kódem jako s řetězcem, je něco špatně.
K čemu tam máš ten eval? A to číslování proměnných? No fuj.

Chceš pracovat s proměnnou, kterou si vyrobíš ve funkci? Tak s ní pracuj normálně, ne? Proč ji dáváš do řetězce? Pokud nadeklaruješ proměnnou v „loadscript“, tak ji ve funkci „load“, která je uvnitř, můžeš úplně normálně používat a bude mít takovou hodnotu, jakou měla v době vykonání „loadscript“. Vyhledej si povídání o closure (česky je to známé jako „lexikální uzávěr“). Nepotřebuješ žádné číslování.

Celý tvůj problém bych zkusil vyřešit takto:
function loadScript(address, callback)
{
  var loaded = false;
  var script = document.createElement("script");
  script.src = address;
  if(callback)
  {
    script.onload = function()
    {
      if(loaded) return;
      loaded = true;
      callback();
    };
    script.onreadystatechange = function()
    {
      if(loaded || this.readyState != "loaded") return;
      loaded = true;
      callback();
    };
  }
  document.body.appendChild(script);
}
Mělo by to fungovat, nezkoušel jsem. Příklad použití:
loadScript("Neškodný.js", function()
{
  alert("Neškodný.js je načten, hurá!");
  nainstalovatTrojana();
});
Witus
Profil *
Díky, ten kód jsem opravdu hodně zprasil na konci, ale ta tvoje úprava je skutečně velice pěkně a hlavně logicky udělaná. Viditelně se mám ještě hodně co učit. Díky moc :)
Witus
Profil *
Z nějakého zvláštního důvodu to teď nejede na internet exploreru...
Chamurappi
Profil
Reaguji na Wituse:
Ukaž. (Ideálně odkazem na chybující stránku.)
Možná bude nutné reagovat i na jiný readyState než „loaded“, třeba má nějaký vliv keš…
Witus
Profil *
Už jsem to zjistil, IE jako správný exot nemá "loaded", ale "complete".

Finální script:

function loadscript(address, callback)
{
  var loaded = false;
  var script = document.createElement("script");
  script.src = address;
  if(callback)
  {
    script.onload = function()
    {
      if(loaded) return;
      loaded = true;
      callback();
    };
    script.onreadystatechange = function()
    {
      if(loaded)
      {
      if(this.readyState != "loaded"&&this.readyState != "complete") return;
      }
      loaded = true;
      callback();
    };
  }
  document.body.appendChild(script);
}
Witus
Profil *
Ok, tak je to ještě trochu jinak - k complete nakonec dojde i opera, jenom na rozdíl od MSIE nepřeskakuje loaded, s čímž byl problém. Proto zcela funkční je ten script od Chamurappiho, jenom místo "loaded" musí být "complete".

function loadscript(address, callback)
{
  var loaded = false;
  var script = document.createElement("script");
  script.src = address;
  if(callback)
  {
    script.onload = function()
    {
      if(loaded) return;
      loaded = true;
      callback();
    };
    script.onreadystatechange = function()
    {
      if(loaded || this.readyState != "complete") return;
      loaded = true;
      callback();
    };
  }
  document.body.appendChild(script);
}
Chamurappi
Profil
Reaguji na Wituse:
IE jako správný exot nemá "loaded", ale "complete"
Dotyčnou událost si celou vymyslel Microsoft, jeho implementace je referenční, těžko může být exotická.
Bude to nejspíš záviset na kešování, jak jsem psal.

Tu podmínku (v příspěvku #16) máš blbě.
Witus
Profil *
Jj všimnul jsem si, proto jsem sem taky hodil nový script - příspěvek 17.
Chamurappi
Profil
Reaguji na Wituse:
Klidně můžeš testovat oba stavy:
if(loaded || (this.readyState != "loaded" && this.readyState != "complete")) return;
Witus
Profil *
Pěkné, už dlouho jsem přemýšlel o tom, jestli by nešlo udělat v podmínce podúrovně :)
Chamurappi
Profil
Reaguji na Wituse:
Trošku mi vrtá hlavou, že jsi vlákno zakládal s takovým vznešeným (a z pohledu zkušenějšího docela oprávněným) požadavkem na eleganci řešení, a nyní tě zjevně překvapilo, že jde v JavaScriptu sestavit triviální podmínka z několika výrazů. Buď vítán v říši za zrcadlem :-)

Ale děkuji ti, že jsi tento problém nadnesl. Netušil jsem, že existuje tak hezké řešení, jaké jsme nakonec dali dohromady. Někde ho určitě použiji.
Witus
Profil *
Reakce na Chamurappi: Mno. Jsem, co se týče javascriptu, samouk (stejně tak i php, ajax apod.), ten zápis s podmínkou je samozřejmě jednoduchý a když vidím, že se to takhle dá zapsat, tak to taky budu ve svých scriptech rozhodně používat (ušetří to hodně řádek kódu navíc), nicméně jsem to jednoduše dosud nevěděl :) . Mám pár příruček se seznamem objektů a příkazů, už jsem napsal spoustu scriptů, nicméně vím, že co se týče syntaxe, mám rozhodně ještě co dohánět. Spoustu scriptů mám napsaných velmi neohrabaně, ačkoliv by to určitě šlo mnohem elegnatněji a občas jsem ani nedošel, díky mým mezerám ve znalosti všech možností zápisu, ke zdárnému konci a zasekl jsem se u polofunkčního scriptu. Rozhodně bych potřeboval najít nějaký zdroj, odkud bych se mohl blíže seznámit s syntaxí, kterou používá většina objektově orientovaných jazyků.

A jsem rád, že můj nápad na dynamické načítání scriptu do dokumentu využije nakonec i někdo jiný. :)
Witus
Profil *
Tím neříkám, že se v tom vůbec nepohybuji, ale spoustu věcí se člověk jen učením z cizích scriptů a separovaných dokumentací nenaučí.
Chamurappi
Profil
Reaguji na Wituse:
Jasně, chápu. Také mi trvalo pár let, než jsem si osahal některé víceméně triviální postupy.
Drajf
Profil *
Díky moc, funguje skvěle a dlouho jsem nemohl najít, jak na to..
Prosím ještě o radu.. V IE i mozille mi to jede, ale v Google Chrome jsem to rozjel až když jsem za jméno přidal měnící se parametr, např.
loadScript("skript.js?i=5",function(){ ...atd... }). 

(Předpokládám, že si prohlížeč soubor kěšuje.) Neexistuje nějaké pěknější řešení??
Drajf
Profil *
Jak na to koukám, asi bych měl poslední příspěvek upřesnit.
Aby se nenačítala stále dokola celá strana, načítají se jen konkrétní proměnné v skript.js. Skript.js je generován dynamicky a mění se v čase. Chrome provede document.body.appendChild(script), ale spustí opět původní soubor, nenatahuje skript.js znova, nechává si původní data. Musím tedy načítat vždy nový soubor odlišený jménem nebo alespoň parametrem (např skript.js?i=1 , skript.js?i=2 , skript.js?i=3 , skript.js?i=4 , atd)..
Witiko
Profil *
Drajf: To je věc čistě prohlížeče, pokud si script netahá pokaždé znovu, tak skutečně budeš muset přidat nějaké POST proměnné, aby si to uvědomil.

Běda vám, jestli to bude blábol:

Za dobu používání jsem script ještě mírně obměnil a stojím před druhým estetickým problémem. Jde mi o odstranění tagu scriptu z HTML kódu stránky po jeho načtení do stránky. Při načítání většího množství externích scriptů začínají mít vývojářské moduly do prohlížečů jako Firebug, Opera dragonflight apod. problémy s plynulostí a navíc se v tom pomalu nedá orientovat, když takovou aplikaci testuji. Aktuální řešení bohužel nefunguje - neodstraňuje child:

function loadscript(address, callback)
{
  var loaded = false;
  var script = document.createElement("script");
  script.type = "text/javascript";
  script.src = address;
  if(callback)
  {
  
    script.onload = function(script)
    {
      if(loaded) return;
      loaded = true;
      try
      {
        document.getElementsByTagName("head")[0].removeChild(script);
      } catch(e) {
        try
        {
          document.body.removeChild(script);
        }catch(e){}
      }
      callback();
    };
    
    script.onreadystatechange = function()
    {
      if(loaded || (this.readyState != "loaded" && this.readyState != "complete")) return;
      loaded = true;
      try
      {
        document.getElementsByTagName("head")[0].removeChild(script);
      } catch(e) {
        try
        {
          document.body.removeChild(script);
        }catch(e){}
      }
      callback();
    };
    
  }
  try
  {
    script = document.getElementsByTagName("head")[0].appendChild(script);
  } catch(e) {
    script = document.body.appendChild(script);
  }
}
Chamurappi
Profil
Reaguji na Witika:
Proč tam máš ty try a catch?

Jde mi o odstranění tagu scriptu z HTML kódu stránky po jeho načtení do stránky.
Což takhle ho odstranit až v době, kdy načítáš další <script>? Udělej z proměnné script globální, před vytvořením nového elementu <script> zkontroluj, jestli existuje starý <script> a pokud ano, odstraň ho.
Witiko
Profil *
Nakonec přepsáno kompletně, chtěl jsem v programu každých 5 sekund pingovat php script, který navrací javascriptový kód, to by s původním skriptem nešlo kvůli zahlcení dokumentu script tagy. Přepsáno objektově, člověk si vytvoří instanci třídy dscript, při prvním zavolání dscript.load() se vytvoří element, při každém další se jen pomocí .replaceChild() nahradí. Pokud se zavolá call, když předchozí script ještě nebyl načtený, uloží se script do fronty.

/*
  
    DScript - Dynamic script loading library
    Copyright (C) 2010  Vít Novotný

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
  
    How to use:
      We create an instance of the dscript class.
      We call the dscript.prototype.load() function with these arguments:
        String url, Number timeout (in milliseconds), function callback(boolean loaded) {}

*/

function dscript() {
  this.object = false;
  this.engaged = false;
  // Creating a two-dimensional Array.
  this.queue = new Array();
  // Here are the URLs stored.
  this.queue[0] = new Array();
  // Here are the passed timeouts stored.
  this.queue[1] = new Array();
  // Here are the callback functions stored.
  this.queue[2] = new Array();
}

dscript.prototype.load = function(url, timeout, callback) {

/*
    Firstly we check if the previous script has already been loaded.
    If so, we may continue and define some of the variables.
*/

  if(!this.engaged) {
    this.engaged = true;
    var that = this;
    var loaded = false;
    
/*
    If the "physical" script element haven't been attached to the document
    yet, we will create it and connect the onload and the onreadystatechange
    (MSIE) events with it. Then we append it as a child to the head element.
*/

    if(!this.object) {
      this.object = document.createElement("script");
      this.object.type = "text/javascript";
      this.object.src = url;
      this.object.onload = function(script) {
        if(loaded) return;
        loaded = true;
        window.clearTimeout(timeout);
        if(that.queue[0].length == 0) that.engaged = false;
        else that._load(that.queue[0][0],that.queue[1][0],that.queue[2][0]);
        if(callback && typeof callback == "function") callback(true);
      };
      this.object.onreadystatechange = function() {
        if(loaded || (this.readyState != "loaded" && this.readyState != "complete")) return;
        loaded = true;
        window.clearTimeout(timeout);
        if(that.queue[0].length == 0) that.engaged = false;
        else that._load(that.queue[0][0],that.queue[1][0],that.queue[2][0]);
        if(callback && typeof callback == "function") callback(true);
      };
      timeout = window.setTimeout(function(){
        loaded = true;
        if(that.queue_urls.length == 0) that.engaged = false;
        else that._load(that.queue[0][0],that.queue[1][0],that.queue[2][0]);
        if(callback && typeof callback == "function") callback(false);
      },timeout);
      this.object = document.getElementsByTagName("head")[0].appendChild(this.object);
      
/*
    If the "physical" script element have already been attached to the document,
    we will just create a new element with an altered src attribute and replace
    the old element for the new one (simply changing the src attribute in the
    existing element doesn't work, the new script is not loaded to the document)
*/

    } else {
      var object = document.createElement("script");
      object.type = "text/javascript";
      object.src = url;
      object.onload = function(script) {
        if(loaded) return;
        loaded = true;
        window.clearTimeout(timeout);
        if(that.queue[0].length == 0) that.engaged = false;
        else that._load(that.queue[0][0],that.queue[1][0],that.queue[2][0]);
        if(callback && typeof callback == "function") callback(true);
      };
      object.onreadystatechange = function() {
        if(loaded || (this.readyState != "loaded" && this.readyState != "complete")) return;
        loaded = true;
        window.clearTimeout(timeout);
        if(that.queue[0].length == 0) that.engaged = false;
        else that._load(that.queue[0][0],that.queue[1][0],that.queue[2][0]);
        if(callback && typeof callback == "function") callback(true);
      };
      timeout = window.setTimeout(function(){
        loaded = true;
        if(that.queue[0].length == 0) that.engaged = false;
        else that._load(that.queue[0][0],that.queue[1][0],that.queue[2][0]);
        if(callback && typeof callback == "function") callback(false);
      },timeout);
      document.getElementsByTagName("head")[0].replaceChild(object, this.object);
      this.object = object;
    }
    
/*
    Should the previous script not've been loaded yet, the variables will be stored in the queue.
*/

  } else {
    this.queue[0][this.queue[0].length] = url;
    this.queue[1][this.queue[1].length] = timeout;
    this.queue[2][this.queue[2].length] = callback?callback:false;
  }
}

/*
    This function should not be called manually and handles the queued queries.
*/

dscript.prototype._load = function(url, timeout, callback) {
  var loaded = false;
  var that = this;
  var object = document.createElement("script");
  object.type = "text/javascript";
  object.src = url;
  object.onload = function(script) {
    if(loaded) return;
    loaded = true;
    window.clearTimeout(timeout);
    that.queue[0] = that.queue[0].slice(1);
    that.queue[1] = that.queue[1].slice(1);
    that.queue[2] = that.queue[2].slice(1);
    if(that.queue[0].length == 0) that.engaged = false;
    else that._load(that.queue[0][0],that.queue[1][0],that.queue[2][0]);
    if(callback && typeof callback == "function") callback(true);
  };
  object.onreadystatechange = function() {
    if(loaded || (this.readyState != "loaded" && this.readyState != "complete")) return;
    loaded = true;
    window.clearTimeout(timeout);
    that.queue[0] = that.queue[0].slice(1);
    that.queue[1] = that.queue[1].slice(1);
    that.queue[2] = that.queue[2].slice(1);
    if(that.queue[0].length == 0) that.engaged = false;
    else that._load(that.queue[0][0],that.queue[1][0],that.queue[2][0]);
    if(callback && typeof callback == "function") callback(true);
  };
  timeout = window.setTimeout(function(){
    loaded = true;
    that.queue[0] = that.queue[0].slice(1);
    that.queue[1] = that.queue[1].slice(1);
    that.queue[2] = that.queue[2].slice(1);
    if(that.queue[0].length == 0) that.engaged = false;
    else that._load(that.queue[0][0],that.queue[1][0],that.queue[2][0]);
    if(callback && typeof callback == "function") callback(false);
  },timeout);
  document.getElementsByTagName("head")[0].replaceChild(object, this.object);
  this.object = object;
}
« 1 2 »

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: