I load html & JS code dynamically from webservice. I use prototype update method:
placeholder.update(result.Static.html); // which also include javascript code
Immediately after i loaded code, everything works fine:
ProRegResetForm();
alert('reset done');
however, from control declared as
Reset
I am having error: ProRegResetForm is not defined.
SIMPLIFIED TEST CODE (which is also not working):
<script type="text/javascript">
var defkeyword_ZipCode = 'Zip Code';
</script>
test link
It was correct to write code like:
test link
<script type="text/javascript">
functoin somethingcomplex() {
bla bla bla
}
$('element').onclick = function() { somethingcomplex(); }
</script>
Given that your 'simplified test code' did not execute, you likely have a javascript error elsewhere in your code. Javascript code that follows an exception will not execute.
Have you tried examining the script prior to your snippet (as it appears on the client) using Firebug or IE Developer Toolbar?
I had a similar problem and solved it by delimiting the HTML response from the JavaScript with a '^' and inserting them separately.
//=============================================================================
function injectJavascript(src)
{
var scripts = document.getElementById("scripts");
var javascriptSrc = document.createElement("script");
javascriptSrc.setAttribute("type", "text/javascript");
javascriptSrc.setAttribute("language", "JavaScript1.2");
javascriptSrc.innerHTML = src;
scripts.appendChild(javascriptSrc);
}
//=============================================================================
function insertHtmlAndScript(target)
{
if (req.readyState == 4) // 4 == "loaded"
{
if (req.status == 200) // 200 == "Ok"
{
var resp = req.responseText.split("^");
div = document.getElementById(target);
div.innerHTML = resp[0];
injectJavascript(resp[1]);
}
else
{
alert("Could not retreive URL:\n" + req.statusText);
}
}
}
//======================================================================
The only reason I can think of that this works while your injection does not is that the HTML vs. JavaScript interpretation is threaded such that the JavaScript can not bind properly... but that's just a guess.
Related
I think there's some kind of non-fatal bug in my script that's not allowing me to a/ debug the script with Firebug and b/ causes Firefox to constantly display a Connecting... (with the swirly) while I'm on the page. The script seems to run fine though.
Any ideas what could cause that?
<script type="text/javascript">
var xmlHttp;
var xmlDoc;
loadXMLFile();
function loadXMLFile()
{
xmlHttp = new window.XMLHttpRequest();
xmlHttp.open("GET", "myFile.xml", true);
xmlHttp.onreadystatechange = StateChange;
xmlHttp.send(null);
}
function StateChange()
{
if ( xmlHttp.readyState == 4 )
{
xmlDoc = xmlHttp.responseXML;
processXML();
}
}
function processXML()
{
var firstNames = xmlDoc.querySelectorAll("name");
if (firstNames == null)
{
document.write("Oh poo. Query selector returned null.");
}
else
{
for (var i = 0; i < firstNames.length; i++)
{
document.write(firstNames[i].textContent + "<br>");
}
}
}
</script>
All the code in your page is parsed, but not executed before the page is completed. This happens, since you're calling document.write() from onreadystatechange event handler function rather than parsing time.
In this case document.write() implicitly calls document.open(), which wipes all the code out of the page, and only what is left is the text written by document.write(). Also document is left open, which causes the browser being "busy". This can be avoided by using document.close(), but it won't prevent the original content to vanish.
You need to add an element to the body and then use some "real" DOM manipulation method. Something like this:
<div id="qResult"></div>
Then instead of document.write():
document.getElementById('qResult').innerHTML = WHAT_EVER_NEEDED
I'm desperate having spent well over an hour trying to troubleshoot this. I am trying to access a node in the DOM which is created from an ASP.NET control. I'm using exactly the same id and I can see that they match up when looking at the HTML source code after the page has rendered. Here's my [MODIFIED according to suggestions, but still not working] code:
ASP.NET Header
<asp:Content ID="HeaderContent" runat="server" ContentPlaceHolderID="HeadContent">
<script type="text/javascript">
$(document).ready(
var el = document.getElementById('<%= txtBox.ClientID %>');
el.onchange = alert('test!!');
)
</script>
</asp:Content>
ASP.NET Body
<asp:TextBox ID="txtBox" runat="server"></asp:TextBox>
Resulting Javascript & HTML from above
<script type="text/javascript">
$(document).ready(
var el = document.getElementById('MainContent_txtBox');
el.onchange = alert('test!!');
)
</script>
...
<textarea name="ctl00$MainContent$txtBox" id="MainContent_txtBox"></textarea>
I can only assume that the script is loading before the control id has been resolved, yet when I look at the timeline with Chrome's "Inspect Element" feature, it appears that is not the case. When I created a regular textarea box to test and implement the identical code (different id of course), the alert box fires.
What on earth am I missing here? This is driving me crazy >.<
EDIT: Wierd code that works, but only on the initial page load; firing onload rather than onchange. Even jQuery says that .ready doesn't work properly apparently. Ugh!!
$(document).ready(function() {
document.getElementById('<%= txtBox.ClientID %>').onchange = alert('WORKING!');
})
Assuming the rendered markup does appear in that order, the problem is that the element doesn't yet exist at the time your JavaScript is attempting to locate it.
Either move that JS below the element (preferably right at the end of the body) or wrap it in something like jQuery's document ready event handler.
Update:
In response to your edits, you're almost there but (as others have mentioned) you need to assign a function to the onchange event, not the return result of alert(). Something like this:
$(document).ready(function() {
// Might as well use jQuery to attach the event since you're already using
// it for the document ready event.
$('#<%= txtBox.ClientID %>').change(function() {
alert('Working!');
});
});
By writing onchange = alert('Working');, you were asking JavaScript to assign the result of the alert() method to the onchange property. That's why it was executing it immediately on page load, but never actually in response to the onchange event (because you hadn't assigned that a function to run onchange).
Pick up jQuery.
Then you can
$(function()
{
var el = document.getElementById('<%= txtBox.ClientID %>');
el.onclick() { alert('test!!'); }
});
Other answers have pointed out the error (attempting to access DOM nodes before they are in the document), I'll just point out alternative solutions.
Simple method
Add the script element in the HTML below the closing tag of the element you wish to access. In its easiest form, put it just before the closing body tag. This strategy can also make the page appear faster as the browser doesn't pause loading HTML for script. Overall load time is the same however, scripts still have to be loaded an executed, it's just that this order makes it seem faseter to the user.
Use window.onload or <body onload="..." ...>
This method is supported by every browser, but it fires after all content is loaded so the page may appear inactive for a short time (or perhaps a long time if loading is dealyed). It is very robust though.
Use a DOM ready function
Others have suggested jQuery, but you may not want 4,000 lines and 90kb of code just for a DOM ready function. jQuery's is quite convoluted so hard to remove from the library. David Mark's MyLibrary however is very modular and quite easy to extract just the bits you want. The code quality is also excellent, at least the equal of any other library.
Here is an example of a DOM ready function extracted from MyLibrary:
var API = API || {};
(function(global) {
var doc = (typeof global.document == 'object')? global.document : null;
var attachDocumentReadyListener, bReady, documentReady,
documentReadyListener, readyListeners = [];
var canAddDocumentReadyListener, canAddWindowLoadListener,
canAttachWindowLoadListener;
if (doc) {
canAddDocumentReadyListener = !!doc.addEventListener;
canAddWindowLoadListener = !!global.addEventListener;
canAttachWindowLoadListener = !!global.attachEvent;
bReady = false;
documentReady = function() { return bReady; };
documentReadyListener = function(e) {
if (!bReady) {
bReady = true;
var i = readyListeners.length;
var m = i - 1;
// NOTE: e may be undefined (not always called by event handler)
while (i--) { readyListeners[m - i](e); }
}
};
attachDocumentReadyListener = function(fn, docNode) {
docNode = docNode || global.document;
if (docNode == global.document) {
if (!readyListeners.length) {
if (canAddDocumentReadyListener) {
docNode.addEventListener('DOMContentLoaded',
documentReadyListener, false);
}
if (canAddWindowLoadListener) {
global.addEventListener('load', documentReadyListener, false);
}
else if (canAttachWindowLoadListener) {
global.attachEvent('onload', documentReadyListener);
} else {
var oldOnLoad = global.onload;
global.onload = function(e) {
if (oldOnLoad) {
oldOnLoad(e);
}
documentReadyListener();
};
}
}
readyListeners[readyListeners.length] = fn;
return true;
}
// NOTE: no special handling for other documents
// It might be useful to add additional queues for frames/objects
else {
if (canAddDocumentReadyListener) {
docNode.addEventListener('DOMContentLoaded', fn, false);
return true;
}
return false;
}
};
API.documentReady = documentReady;
API.documentReadyListener = documentReadyListener;
API.attachDocumentReadyListener = attachDocumentReadyListener;
}
}(this));
Using it for your case:
function someFn() {
var el = document.getElementById('MainContent_txtBox');
el.onclick = function() { alert('test!!');
}
API.attachDocumentReadyListener(someFn);
or an anonymous function can be supplied:
API.attachDocumentReadyListener(function(){
var el = document.getElementById('MainContent_txtBox');
el.onclick = function() { alert('test!!');
};
Very simple DOM ready functions can be done in 10 lines of code if you just want one for a specific case, but of course they are less robust and not as reusable.
i have a problem with this script in firefox 4. I test the same script in chrome and it works, but in FF the load never stops, maybe some problem with the code
<script type="text/javascript">
$(document).ready(function(){
var somevar = 'some info';
var someothervar = 'some other info';
var data = "var1=somevar&var2=someothervar";
$.post("chart.php", data, function(theResponse){
if (theResponse == 'sim') {
document.write("test");
}
else {
document.write("testone");
}
});
});
</script>
php file have a simple echo "sim";
thanks
You really can't get away with using "document.write()" for such testing. Change your code like this:
$(document).ready(function(){
var somevar = 'some info';
var someothervar = 'some other info';
var data = "var1=somevar&var2=someothervar";
$.post("chart.php", data, function(theResponse){
if (theResponse == 'sim') {
alert("test");
}
else {
alert("testone");
}
});
});
Because the response to the request is very likely to be received after the browser has finished with the original page, the call to "document.write()" will have the effect of obliterating that page.
Beyond that, you can try the TamperData plugin for Firefox (if it's been updated for FF4 ...) to watch the progress of HTTP requests. FireBug will show you XHR requests too.
today I've been working on loading dynamic javascript code (files). The solution I use is :
function loadScript(scriptUrl) {
var head = document.getElementsByTagName("head")[0];
var script = document.createElement('script');
script.id = 'uploadScript';
script.type = 'text/javascript';
script.src = scriptUrl;
head.appendChild(script);
}
the problem with this is that I don't know how to make sure WHEN the script contents are executed. Since the script contains classes (well JS classes), I wrote down a function that is called through setTimeout and checks if those objects are defined. This is not flexible as it is not automatical. So? Are there ways to load those scripts and have a reliable notification on when they have been executed?
You can use jquery's getScript and pass a callback function.
$.getScript("test.js", function(){
alert("Script loaded and executed.");
});
See: jquery.
The easiest way short of a JS library is to make an XMLHttpRequest and eval() the return. Your "onload" would be when the data is returned, and "oninit" would be right after you've eval'd.
EDIT: If you want to sequence this, you can create an AssetLoader that takes an array of scripts and won't eval() a script until the one preceding it has been fetched and initialized.
EDIT 2: You can also use the script.onload stuff in the post referenced in the comments. The eval method has a slight advantage in that you can separate and control the load and execution portions of the script import.
EDIT 3: Here's an example. Consider a file called foo.js that contains the following:
function foo () {
alert('bar');
}
Then execute the following from another page in your browser:
function initScript (scriptString) {
window.eval(scriptString);
}
function getScript (url, loadCallback, initCallback, callbackScope) {
var req = new XMLHttpRequest();
req.open('GET', url);
req.onreadystatechange = function (e) {
if (req.readyState == 4) {
if (loadCallback) loadCallback.apply(callbackScope);
initScript.call(null, req.responseText);
if (initCallback) initCallback.apply(callbackScope);
}
}
req.send();
}
function fooScriptLoaded () {
alert('script loaded');
}
function fooScriptInitialized () {
alert('script initialized');
foo();
}
window.onload = function () {
getScript('foo.js', fooScriptLoaded, fooScriptInitialized, null);
}
You will see the alerts "script loaded", "script initialized", and "bar". Obviously the implementation of XMLHttpRequest here isn't complete and there are all sorts of things you can do for whatever scope you want to execute the script in, but this is the core of it.
You could use a counter variable, and a kind of callback:
var scriptsToLoad = 10;
var loadedScripts = 0;
//...
function increaseLoadCount() {
loadedScripts++;
if(loadedScripts == scriptsToLoad) {
alert("start here");
}
}
//script1-10.js
increaseLoadCount();
Maybe a bit nicer than with a timeout..
I want to use jQuery inside a firefox extension,
I imported the library in the xul file like this:
<script type="application/x-javascript" src="chrome://myExtension/content/jquery.js"> </script>
but the $() function is not recognized in the xul file neither do the jQuery().
I googled about the problem and found some solutions but no one did work with me:
http://gluei.com/blog/view/using-jquery-inside-your-firefox-extension
http://forums.mozillazine.org/viewtopic.php?f=19&t=989465
I've also tried to pass the 'content.document' object(which refrences the 'document' object) as the context parameter to the jQuery function like this:
$('img',content.document);
but still not working,
does any one came across this problem before?
I use the following example.xul:
<?xml version="1.0"?>
<overlay id="example" xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
<head></head>
<script type="application/x-javascript" src="jquery.js"></script>
<script type="application/x-javascript" src="example.js"></script>
</overlay>
And here is an example.js
(function() {
jQuery.noConflict();
$ = function(selector,context) {
return new jQuery.fn.init(selector,context||example.doc);
};
$.fn = $.prototype = jQuery.fn;
example = new function(){};
example.log = function() {
Firebug.Console.logFormatted(arguments,null,"log");
};
example.run = function(doc,aEvent) {
// Check for website
if (!doc.location.href.match(/^http:\/\/(.*\.)?stackoverflow\.com(\/.*)?$/i))
return;
// Check if already loaded
if (doc.getElementById("plugin-example")) return;
// Setup
this.win = aEvent.target.defaultView.wrappedJSObject;
this.doc = doc;
// Hello World
this.main = main = $('<div id="plugin-example">').appendTo(doc.body).html('Example Loaded!');
main.css({
background:'#FFF',color:'#000',position:'absolute',top:0,left:0,padding:8
});
main.html(main.html() + ' - jQuery <b>' + $.fn.jquery + '</b>');
};
// Bind Plugin
var delay = function(aEvent) {
var doc = aEvent.originalTarget; setTimeout(function() {
example.run(doc,aEvent);
}, 1);
};
var load = function() {
gBrowser.addEventListener("DOMContentLoaded", delay, true);
};
window.addEventListener("pageshow", load, false);
})();
The following solution makes it possibile to use jQuery in contentScriptFile
(Targetting 1.5 Addon-sdk)
In your main.js:
exports.main = function() {
var pageMod = require("page-mod");
pageMod.PageMod({
include: "*",
contentScriptWhen: 'end',
contentScriptFile: [data.url("jquery-1.7.1-min.js") , data.url("notifier.js") , data.url("message.js")],
onAttach: function onAttach(worker) {
//show the message
worker.postMessage("Hello World");
}
});
};
In your message.js :
self.on("message", function(message){
if(message !== "undefined"){
Notifier.info(message);
}
});
Some pitfalls you need to watchs out for:
The order of the contentScriptFile array. if message.js would be placed first: jQuery won't be reconized.
Do not place a http:// url in the data.url (this does not work)!
All your javascript files should be in the data folder. (only main.js should be in lib folder)
There is an excellent article in the mozillaZine forums that describes this step-by-step: http://forums.mozillazine.org/viewtopic.php?f=19&t=2105087
I haven't tried it yet, though so I hesitate to duplicate the info here.
Turns out the current top-answer by #sunsean does not work as expected when it comes to handling multiple loads. The function should properly close over the document and avoid global state.
Also, you have to call jQuery.noConflict(true) to really avoid conflicts with other add-ons!
This is who I would write it (then again, I would avoid jquery (in add-ons) like the plague...).
First the overlay XUL
<?xml version="1.0"?>
<overlay id="test-addon-overlay" xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
<script type="text/javascript" src="jquery.js"/>
<script type="text/javascript" src="overlay.js"/>
</overlay>
And then the overlay script:
// Use strict mode in particular to avoid implicitly var declarations
(function() {
"use strict";
// Main runner function for each content window.
// Similar to SDK page-mod, but without the security boundaries.
function run(window, document) {
// jquery setup. per https://stackoverflow.com/a/496970/484441
$ = function(selector,context) {
return new jq.fn.init(selector,context || document);
};
$.fn = $.prototype = jq.fn;
if (document.getElementById("my-example-addon-container")) {
return;
}
let main = $('<div id="my-example-addon-container">');
main.appendTo(document.body).text('Example Loaded!');
main.click(function() { //<--- added this function
main.text(document.location.href);
});
main.css({
background:'#FFF',color:'#000',position:'absolute',top:0,left:0,padding:8
});
};
const log = Components.utils.reportError.bind(Components.utils);
// Do not conflict with other add-ons using jquery.
const jq = jQuery.noConflict(true);
gBrowser.addEventListener("DOMContentLoaded", function load(evt) {
try {
// Call run with this == window ;)
let doc = evt.target.ownerDocument || evt.target;
if (!doc.location.href.startsWith("http")) {
// Do not even attempt to interact with non-http(s)? sites.
return;
}
run.call(doc.defaultView, doc.defaultView, doc);
}
catch (ex) {
log(ex);
}
}, true);
})();
Here is a complete add-on as a gist. Just drop in a copy of jquery and it should be good to go.
I think this is what Eric was saying, but you can load Javascript from the URL directly.
javascript:var%20s=document.createElement('script');s.setAttribute('src','http://YOURJAVASCRIPTFILE.js');document.getElementsByTagName('body')[0].appendChild(s);void(s);
Im assuming you want your extension to load JQuery so you can manipulate the page elements easily? My company's labs has something that does this using Javascript directly here: http://parkerfox.co.uk/labs/pixelperfect
It may be bad practice, but have you considered including it inline?
Instead of
$('img',content.document);
you can try
$('img',window.content.document);
In my case it works.