Debug DOM mutations with Firebug before page load - javascript

I'm having problems with debugging DOM changes introduced by some JavaScript code I'm running. Somewhere in the code an element's class is changed, and I'm trying to pinpoint where exactly. Unfortunately, the new class name is so generic that searching through all the JS code gives too many results to be a viable option.
I've tried debugging a bit with Firebug, but despite the nice "Break on Attribute Change" feature, I can't get it to work in a way I would want. The Firebug demo works correctly, but it's a post load situation.
The problem seems to be that I want to watch for mutations before the page is fully loaded. I assume that the changes occur somewhere in $(document).ready(), so it's in the DOM, but I can't select elements for UI breakpoints as would be the case with the demo (after page load).
Is there some way to debug this kind of situation other than grepping/going through the code by hand?

I propose that you remove the target element where its classname is changed. With some luck, you may be able to generate an error in the JavaScript code, so you will find where is your problem.
Otherwise, if it's done by JQuery (addClass), you may want to modify the JQuery code itself just to figure out the callstack.
The code would look like this (make sure this code is the first code called after JQuery inclusion):
(function () {
var addClass = jQuery.fn.addClass;
jQuery.fn.addClass = function (value) {
for (var i = 0, l = this.length; i < l; i++) {
// Here you put your method to start the debugger if you get your right element
if (this[i].id === "abc") {
debugger;
}
}
addClass(value);
}
})();
Hope that helps.

This answer may sound pretty lame, but I honestly think the best solution for bugs like this is "deconstructing" your program. Just make a copy of the entire project, then rip it apart. Take out chunks of code one by one. Turn function calls into stub functions or whatever to keep things running. Find the minimal amount of code that triggers the bug. Then the solution should be obvious.

Have you considered adding a mutation event? The event I think you want, DOMAttrModified, is not supported in webkit, so you might have to test with Firefox or Opera. In fact it is deprecated in DOM level 3.
There are two jQuery plugins for mutation events here (documentation) and here but since you want to do this before page load they might not be the answer.
You are probably best writing your own JavaScript bind for this event - there is an example in the answer to is there an alternative to DOMAttrModified that will work in webkit
I hope this helps.

If you want to use the "Break on Attribute Change" feature to debug, you can do the following:
Comment out all JS in the page (which is hopefully all in the head) except for the base jQuery load.
Load the page and set your watch points.
Add a button, which fires the JS, to the HTML. Or, optionally fire it from the console.
Trigger the JS load/fire. Hopefully your watch and break points will fire as desired.
For example, suppose your page loads/has:
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.5.2/jquery.min.js" type="text/javascript"></script>
<script src="/Library_X.js" type="text/javascript"></script>
<script src="/MyJS.js" type="text/javascript"></script>
Then you could run this code in the console after setting the watch points:
function addJS_Node (text, s_URL)
{
var scriptNode = document.createElement ('script');
scriptNode.type = "text/javascript";
if (text) scriptNode.textContent = text;
if (s_URL) scriptNode.src = s_URL;
document.head.appendChild (scriptNode);
}
addJS_Node (null, '/Library_X.js');
//-- Possible pause here.
addJS_Node (null, '/MyJS.js');
// etc.
Or temporarily code a button that fires the same JS, into the page's HTML.

Related

Remove Attribute-defined `onload` from `<body>`

I've been working on this for a few hours and am at the point where I feel like I might be trying to accomplish something unworkable. Any advice or insight is definitely appreciated!
The use case I'm targeting requires interrupting the browser's DOM parsing before it has a chance to draw the <body> element. To accomplish this, I've used this one-liner as the first script called in my <head> element:
document.replaceChild(document.createElement('html'), document.children[0]);
This successfully prevents the browser from calling any subsequent scripts, and the <body> element remains un-rendered. However, if the markup includes an onload attribute on the <body> element, that function or code is still called by the browser.
As the context which intercepts DOM parsing is in <head>, I can't use document.body.removeEventListener() to drop the event listener, because the body element doesn't exist yet. Setting window.onload to null has no effect, and neither does trying to intercept setting of window.onload using Object.defineProperty(window, 'onload', {...}). In each case, the onload function or statements still get called.
To preempt the obvious question as to why I can't just avoid using <body onload="...">, the use case is for developer tooling, so I'm trying to create contingency for certain absolute conditions. If it can't be done, it isn't killer to my project, but I'd sure like to know why it can't be done.
Thanks in advance!
You can use Document readystate at the interactive state, and overwrite the onload attribute.
document.onreadystatechange = () => {
if (document.readyState === 'interactive')
window.onload = null;
}
(Added your code from your comment for completeness.)

How to remove HTML script tag with open type [duplicate]

How can I remove script elements before they are being executed?
I thought about using the DOMNodeInserted event, but apparently it doesn't catch script elements. I've also tried using the jQuery livequery plugin like that:
$("script").livequery(function () {
$(this).remove();
});
It did remove the script element, but after it was executed.
I'm looking for a cross-browser solution, but I'm not even sure if that's possible. I read about Mutation Observers which seems close enough but I'm not sure if it can solve my problem.
It would be even better if there was a way to modify the script content before it is being executed without removing and recreating it.
Removing a script element does not do anything. If you can somehow access a script element, it was executed a long time ago and removing it will have no effect.
So we need to work around it. If your script element is at the top of the page like this:
<head>
<script src="yourscript.js"></script>
You could make a synchronous ajax request to the same page, so you can parse its content into a new document, modify all script tags and then replace
the current document with the modified document.
var xhr = new XMLHttpRequest,
content,
doc,
scripts;
xhr.open( "GET", document.URL, false );
xhr.send(null);
content = xhr.responseText;
doc = document.implementation.createHTMLDocument(""+(document.title || ""));
doc.open();
doc.write(content);
doc.close();
scripts = doc.getElementsByTagName("script");
//Modify scripts as you please
[].forEach.call( scripts, function( script ) {
script.removeAttribute("src");
script.innerHTML = 'alert("hello world");';
});
//Doing this will activate all the modified scripts and the "old page" will be gone as the document is replaced
document.replaceChild( document.importNode(doc.documentElement, true), document.documentElement);
Unfortunately this cannot be set up in jsfiddle or jsbin. But you should be able to copy paste this code exactly as it is into this
page's console in google chrome. You should see the alerts and when you inspect the live dom, each script was modified.
The difference is that we are running this after scripts have been executed on the page, so the old scripts should still have a working effect on the page.
That's why, for this to work, you need to be the very first script on the page to do it.
Tested to work in google chrome. Firefox is completely ignoring the doc.write call for some reason.
i donot know what you are trying to do. But it is better to load them on request rather than delete on some conditions.
$.getScript('helloworld.js', function() {
$("#content").html('
Javascript is loaded successful!
');
});
If you wants to remove scripts before there execution, its not possible.
But what you can do is, remove script programatically on a condition & if have an issue with memory-leaks, then you can call below code before remove script.
var var1 = 'hello';
var cleanAll = function () {
delete window.var1;
delete window.cleanAll;
};
// unload all resources
cleanAll();

onload function init not found

I'm encountering a strange issue. I am developing a books application and using javascript onload. I read somewhere that its best to include your javascript at the end of the html. This works for most of the html loaded. However some complain that onload init() not found. This gets solved if i include the javascript in the html head. But than other htmls start behaving strangely. onload gets called before the page is fully loaded. i dont get the correct scroll width. Please suggest what could be worng. Whats the best way of including javascripts. Thanks
html is as follows
columizer id use css column-width which i've defined like this.
css style below
#columnizer
{
width:290px;
height:450px;
column-width:290px;
column-gap:10px;
word-wrap:break-word;
}
Javascript onload is defined like this.
function init()
{
docScrollWidth = document.getElementById('columnizer').scrollWidth;
document.getElementsByTagName('body')[0].style.width = docScrollWidth + "px";
window.external.notify(str);
}
Since the actual answer was in my comment, I'll add that to my answer:
My guess is that you're doing something like window.onload = init(); instead of window.onload = init; and the init function will have to be declared before you do that assignment. You assign function references without the parens. Using the parens causes it to get executed immediately.
You say you're using this code:
docScrollWidth = document.getElementsByTagName('body')[0].style.width
The main problem with this is that style.width ONLY reads a style attribute set directly on the body object. It doesn't get the width of the object as calculated by layout or CSS rules.
So, what you should use instead really depends upon what you're trying to do. The body width will nearly always be the same or more than the window width unless your content is entirely fixed width. So, that makes me wonder what you're trying to accomplish here? What you should use instead depends upon what you're really trying to do.
FYI, document.body is a direct reference to the body object so you don't need document.getElementsByTagName('body')[0].
First, let me define the problem. The window.onload event is used by programmers to kick-start their web applications. This could be something trivial like animating a menu or something complex like initialising a mail application. The problem is that the onload event fires after all page content has loaded (including images and other binary content). If your page includes lots of images then you may see a noticeable lag before the page becomes active. What we want is a way to determine when the DOM has fully loaded without waiting for all those pesky images to load also.
Mozilla provides an (undocumented) event tailor-made for this: DOMContentLoaded. The following code will do exactly what we want on Mozilla platforms:
// for Mozilla browsers
if (document.addEventListener) {
document.addEventListener("DOMContentLoaded", init, false);
}
So what about Internet Explorer?
IE supports a very handy (but non-standard) attribute for the tag: defer. The presence of this attribute will instruct IE to defer the loading of a script until after the DOM has loaded. This only works for external scripts however. Another important thing to note is that this attribute cannot be set using script. That means you cannot create a script using DOM methods and set the defer attribute – it will be ignored.
Using the handy defer attribute we can create a mini-script that calls our onload handler:
<script defer src="ie_onload.js" type="text/javascript"></script>
The contents of this external script would be a single line of code to call our onload event handler:
init();
There is a small problem with this approach. Other browsers will ignore the defer attribute and load the script immediately. There are several ways round this. My preferred method is to use conditional comments to hide the deferred script from other browsers:
<!--[if IE]><script defer src="ie_onload.js"></script><![endif]-->
IE also supports conditional compilation. The following code is the JavaScript equivalent of the above HTML:
// for Internet Explorer
/*#cc_on #*/
/*#if (#_win32)
document.write("<script defer src=ie_onload.js><\/script>");
/*#end #*/
So far so good? We now need to support the remaining browsers. We have only one choice – the standard window.onload event:
// for other browsers
window.onload = init;
There is one remaining problem (who said this would be easy?). Because we are trapping the onload event for the remaining browsers we will be calling the init function twice for IE and Mozilla. To get around this we should flag the function so that it is executed only once. So our init method will look something like this:
function init() {
// quit if this function has already been called
if (arguments.callee.done) return;
// flag this function so we don't do the same thing twice
arguments.callee.done = true;
// do stuff
};
I’ve provided a sample page that demonstrates this technique.

Dynamically loaded <script> tag not being used in WebKit browsers

It seems this is a known problem and has been asked several times before here in SO however I do not see anything specific to jQTouch so I thought I would give it a try.
jQT will dynamically load pages when a link is clicked. In this page I would like to include something like
<script>
$.include('javascriptfile.js', function() {alert('do something with results of this file to an already existing div element');};
</script>
The $.include is a jquery plugin I found that mimics the $.load with a few more smarts added to it. Tested to work on FF but not in Chrome or most importantly, Safari.
The alert is never displayed. FireBug never shows the javascript even being loaded. If I put an alert before the $.include I still do not see anything.
I have tried an onclick/ontap event that would then run this code that was included in the head tag, no luck.
Edit: I am using the r148 revision of jQT. This was working prior to moving to this version, i believe.
Did you try to add the javascript file using one of these two methods:
Static Way:
<script type="text/javascript">
function staticLoadScript(url){
document.write('<script src="', url, '" type="text/JavaScript"><\/script>');
}
staticLoadScript("javascriptfile.js");
modifyDivFn(processFnInFile());
</script>
Dynamic way:
<script type="text/javascript">
function dhtmlLoadScript(url){
var e = document.createElement("script");
e.src = url;
e.type="text/javascript";
document.getElementsByTagName("head")[0].appendChild(e);
}
onload = function(){
dhtmlLoadScript("javascriptfile.js");
modifyDivFn(processFnInFile());
}
</script>
After the include you can call a function that does the processing you want (that being processFnInFile()) which result will be passed to modifyDivFn (and modify the div you want.) You could do this in one function, just to illustrate the idea.
Source: Dynamically Loading Javascript Files
Well Geries, I appreciate your help but ultimately the answer required a drastic rethinking of how I was using JQTouch. The solution was to move everything to an onclick event and make all the hrefs link to #. This might be what you were talking about Geries.
In the onclick function I do the logic, preloading, loading of the page through my own GET through jquery, then use the public object jQT.goTo(div, transition). This seems to get around the WebKit bugs or whatever I was running into and this now owrks on FireFox, Chrome, Safari, iPhone, and the lot.
I do run into a few animation issues with JQT but I think these are known issues that I hope Stark and the gang at JQTouch are working on.

How do I add an external js file with a parameter

I want to give a minimal js code to random websites so that they can add a widget.
The code needs to run after the main page loads and include a parameter with the domain name. ( I don't want to hardcode it)
One option is to add this code just before the </body> (so It will run after the page loads):
<script type="text/javascript" id="widget_script">
document.getElementById('widget_script').src=location.protocol.toLowerCase()+"//mywebsite.com/?u="+encodeURIComponent(window.location.host);
</script>
This works in IE but not in Firefox. I see with Firebug that the src property is created correctly but the script from my site is not loaded.
My question is : what is the best way to do that ? (preferably by putting minimal lines on the header part.)
To further clarify the question: If I put a script on the header part, how do I make it run after it is loaded and the main page is loaded? If I use onload event in my script I may miss it because it may load after the onload event was fired.
You can try to statically include the script with document.write (is an older technique and not recommended to use as it can cause problems with more modern libraries):
var url = location.protocol.toLowerCase() +
"//mywebsite.com/?u="+encodeURIComponent(window.location.host);
document.write('<script src="', url, '" type="text/javascript"><\/script>');
Or with dynamically created DOM element:
var dynamicInclude = document.createElement("script");
dynamicInclude.src = url;
dynamicInclude.type = "text/javascript";
document.getElementsByTagName("head")[0].appendChild(dynamicInclude);
Later edit:
To ensure the script is run after onload this can be used:
var oldWindowOnload = window.onload;
window.onload = function() {
oldWindowOnload();
var url = location.protocol.toLowerCase() +
"//mywebsite.com/?u="+encodeURIComponent(window.location.host);
var dynamicInclude = document.createElement("script");
dynamicInclude.src = url;
dynamicInclude.type = "text/javascript";
document.getElementsByTagName("head")[0].appendChild(dynamicInclude);
}
I do not believe it can be shorter than this, apart from shorter variable names :)
Why not use the getScript method of jQuery to do the loading? If you don't want to be dependant on jQuery, you can trace through the source to learn how they tackled it.
Viewing a widely used library is always going to show you solutions to problems you didn't know you had. For example, you can see how jQuery manages to generate a callback when the script is loaded, and how it avoids a purported memory leak in IE.
You probably want to be implementing the non-blocking script technique. Essentially instead of modifying the src of a script, you're going to create and append a whole new script element.
Good write up here and there are standard ways to do this in YUI and jQuery. It's quite straightforward with only one gotcha which is well understood (and documented at that link).
Oh and this:
One option is to add this code just
before the </body> (so It will run
after the page loads):
...is not technically true: you're just making your script the last thing in the loading order.

Categories

Resources