removing duplicate script from page - asp.net

I am trying to make use of the yahoo exceptional performance rule : avoiding duplicate script
To do so i would like to be able to know whether or not a script was already added to the page before injecting it in the page. It looks like i can't figure what has been added in asp.net code behind unless i have a scriptmanager added to the page. but i would like to avoid using asp.net AJAX. From the description of the rule, it looks like it is something possible in php though.
Assuming that i can't do the check in my code behind, i was considering using jQuery $.getString function but it doesn't check before fetching the script. If i was to choose the javascript file, would i have to parse the whole http response in order to figure out which script was loaded on the page?

If the page is registering the scripts with the ASP.NET Page.ClientScript Register APIs then you can use Page.ClientScript.IsClientScriptIncludeRegistered. On the other hand, if you are using those APIs you don't really need to call it, since it already ensures only one of each is registered.
http://msdn.microsoft.com/en/us/library/system.web.ui.clientscriptmanager.isclientscriptincluderegistered.aspx
If the page just has regular ole script elements statically in the markup, and you need to detect if a script is loaded on the client side, you will have to get all the script elements on the page and look at their .src values. The thing with that is that some browsers automatically resolve that url to a full path, not just the one you declared. So, you can account for that in various ways -- you can just search for the end of the string being the script you want, or you can cause the url you want to compare with to also be resolved by setting it onto a dynamically created script element (which you never add to the DOM but is still resolved for you).
This is just off the top of my head, sorry if I get something wrong:
var s = document.createElement("script");
s.src = "foo.js";
var loaded, scripts = document.getElementsByTagName("script");
for (var i = 0; i < scripts.length; i++) {
if (scripts[i].src === s.src) {
loaded = true;
break;
}
}
if (loaded) {
// this script is already loaded
// assuming you dont have multiple copies in different locations
}

You don't need to use any client-side scripting to do this... you can do this in your code behind using the ClientScriptManager without needing to make use of ASP.NET AJAX (I think you're confusing ClientScriptManager with ScriptManager*) in your control/page just use:
ClientScript.RegisterClientScriptInclude("some-script", "myScript.js");
or from your user controls:
Page.ClientScript.RegisterClientScriptInclude("some-script", "myScript.js");
This will use the key "some-script" & only register one copy of the script on the page.
*To be clear I think the confusion is arrising from the difference between these:
ClientScriptManager if a server-side helper class which is used to manage client side scripts (in other words its whole purpose is to do exactly what you are trying to do). It is accessed via the Page's ClientScript property.
ScriptManager is a Control used to aid client side Ajax scripting in ASP.NET AJAX
(hell I even confused myself & gave the wrong example code initially)

well that wouldn't actually work in a master detail scenario with multiple web user controls.
Then you wouldn't have control over who has to do the script initialization if the web user control is dynamic.
It's easier to link once, but a developer would have to weigh his options between ClientManager and using a script load.

yeah you have to parse the whole response...
why don't you create a javascript file and put all of your javascript there and then import that javascript file in your code??? in this way you can get rid of duplicate script insertion.

Related

Accessing a control in asp.net content page through Javascript

I have a form in my content page for which I am doing some client side validations via Javascript. The Javascript behaves as expected if I place the JS code directly in the content page. But if I place the JS code in it's own file and try accessing that from the content/master page (through the script tag's src attribute), I get a run time error when the validation function in JS being called.
To be specific, I get the below error.
Microsoft JScript runtime error: Objected expected/required at this line - document.getElementById('<%=txtemailId.ClientID %>').value
txtemailId is in the content page.
Javascript code is placed in validation.js and accessed via master page.
The reason I guess is that when .net is parsing the files, it is unable to substitute txtemailId.ClientID with the client side value that would be generated later on. So, how should one go about it? Thanks!
The answer to this is quite simple.
Within your content page declare an in-line JScript variable, make sure this is above your tag.
<script>
var emailClientId = <%=txtemailId.ClientID%>;
</script>
Within your include.js file make use of the globally scoped emailClientId variable.
Obviously this is a bit clumsy because not all of the JScript code is contained within the include.js file, which makes it difficult to debug / diagnose as it is not clear specifically where the value is coming from. You should clearly comment / document this within your code to make maintenance of he codebase easier in the future.
You're right, the code <%=txtemailId.ClientID %> will only get replaced with the real client ID by ASP.Net if this code is in an ASP.Net file. So if you put this javascript in a separate .js file, ASP.Net will know nothing about it, and the code will not be parsed.
The easiest way to achieve this is to make the control IDs parameters of your functions. This way you can have the calling javascript code in your .aspx (or .ascx) file, along with the <%=txtemailId.ClientID %> code.
Standalone .js files are not run through the ASPX parser, so your expression is not being evaluated.
To solve the problem, you could set global variables (or function parameters, or something else) in the ASPX files with the IDs of the controls, then use them in the standalone .js file.
Alternatively, you could uses class names (which don't get mangled) instead of IDs. (This is very simple using jQuery)
An approach like this is best suited for something you want to achieve:
validation.js
var Validation = {
EmailId: null,
Validate: function() {
var email = document.getElementById(EmailId).value;
}
}
page.aspx
Validation.EmailId = '<%=txtemailId.ClientID %>'; //initialize
Validation.Validate(); //whenever you want to validate

ASP.NET Web User Control with Javascript used multiple times on a page - How to make javascript functions point at the correct controls

I think I summed up the question in the title. Here is some further elaboration...
I have a web user control that is used in multiple places, sometimes more than once on a given page.
The web user control has a specific set of JavaScript functions (mostly jQuery code) that are containted within *.js files and automatically inserted into page headers.
However, when I want to use the control more than once on a page, the *.js files are included 'n' number of times and, rightly so, the browser gets confused as to which control it's meant to be executing which function on.
What do I need to do in order to resolve this problem? I've been staring at this all day and I'm at a loss.
All comments greatly appreciated.
Jason
If the issue is simply that the same file is being embedded multiple times and causing conflict, look into using RegisterClientScriptInclude. If you use the same identifier for all of your calls to RegisterClientScriptInclude only one instance of that file will be embedded:
http://msdn.microsoft.com/en-us/library/2552td66.aspx
However, if the issue is that your methods are being called but they don't know what controls on the page to operate on then you need to figure out how to provide your JS some context. Define a JavaScript object that represents your control on the client-side, and on the server-side emit the call that will instantiate it with the client IDs of the controls you'll be operating on.
We are using CustomValidator to validate User Control. The control works fine until you drop two instances of the Control on the same page, since they reference the exact same JavaScript functions, only one control works. Work around, we appended JavaScript function name with control id.
Validate_SAPDepartment<% =ControlId %>(oSrc, args) {...}
In codebehind, we assinged ClientValidationFunction
CustomValidator1.ClientValidationFunction = "Validate_SAPDepartment" + this.ControlId
This may not be the right approach but it works.
I've had this situation before. You register a separate JavaScript file with the page using the ScriptManager. You can stream this as a resource file embedded into the dll if you wish.
Then you only call into the functions from your control.
Otherwise a completely separate jquery file may also work.

asp.net pass javascript code from database

The Question:
Is there a way that I can pass a JavaScript function into a page (from a database) to be run?
Background info
I have developed a metadata driven asp.net mvc web site (basically I change data in the db, and the pages get generated on the fly).
Unfortunately I have one page that has some routing requirements (i.e. if you choose option 1, then hide item x)
I could solve this by having a custom page, but it would be nice if I could come up with a more robust solution.
So what I am thinking is that I can attach an onchange event handler (based on a metadata value), and then somehow have some code that is executed based on that onchange event.
You can have server-side code render static javascript yes, but you can't change the javascript when on the client due to a change in requirements; you have to render JS code that's dynamic enough to handle all the interactions you may need to do. By server-side code, that means any code as well as any external resources (ie. database). Though, I'm not sure storing static JS code in the DB is buying you anything more than you could already have by storing the JS in a file, and having JS components smart enough to have these different interactions within that file...
So yes server-side storage can render out static JavaScript, but JS can't change from server-side code dynamically, unless your JS code call a web service on the server side.
Make sense?
Its not really the safest thing to do, but using JavaScript's eval() function, you can pass in whatever you want and have it run.
Another idea is to generate a javascript file on the server (whether by getting it from the database or by some other method), and on the page, add a script tag that calls a handler (ashx file) that renders the javascript file.
On the client, you would have this javascript:
var h = document.getElementsByTagName("head")[0];
var js = document.createElement("script");
js.type = "text/javascript";
js.src = "scriptfilegenerator.ashx";
h.appendChild(js);
And on the server (ashx file) you would have this code:
context.Response.AddHeader("content-disposition", "inline; filename=scriptfile.js");
context.Response.ContentType = "text/javascript";
context.Response.Write("your script here");

What's the best way to embed constant server-side tags inside javascript code?

I have a bunch of javascript functions which depend on some server-side constants (such as strings from my resources file). While I was developing them I kept the javascript code in the view header, so I could simply used server-side tags inside my javascript code, but now I'd like to move the javascript functions to a separate file.
I can't use a regular js file since that would not be interpreted by the server, making the server tags embedded there useless. I don't want to define variables in the page either since that seems way to awkward and error-prone.
What I've done instead is to create a new aspx file, put the javascript functions there and include that aspx file instead of a regular js file in my master template. It seems a bit unorthodox, but it seems to work fine.
Is there any downside to my approach that I have not taken into account? Or any better (less obscure) method?
Edit Bonus question: Should I use a DOCTYPE inside the included scripts file? After all, the scripts file is included by a script tag already. I tried to mimic regular js files, so I did not specify any DOCTYPE.
The file extension, be it js, aspx, ashx, bla, foo, whatever isn't all that important. If you have server side generated javascript that isn't specific to a page, then creating an ASPX page to render the javascript should be okay.
We'll often use HTTP handlers to generate dynamic javvascript in our systems. We also make sure to set the response headers to text/javascript to let the client browser know that we are sending back javascript.
Using a view, with MVC's templating capabilities is a great way to accomplish this. It is easy to maintain, well understood and gets the job done fast. The only real trick is to get it to serve the correct content-type. Doing something like this:
public ActionResult ConfigurationSettings()
{
Response.ContentType = "text/javascript";
return View(Configuration);
}
Actually gets you text/html content type. The trick is to make sure you get the last word, add this to your controller:
protected override void Execute(System.Web.Routing.RequestContext requestContext)
{
base.Execute(requestContext);
requestContext.HttpContext.Response.ContentType = "text/javascript";
}
And you will get the correct content type; ViewResult seems to force it to go text/html.
We use the ScriptDataBlock control from JsonFx to emit variables into the page itself. It acts like a dictionary where the key is the variable name (e.g. "MyNamespace.myVar") and the value is a normal C# value (including whole objects). The control emits appropriate namespace generation and type conversion to native JavaScript types. So in that sense it ends up not being "awkward" or "error prone":
myDataBlock["MyApp.myVar"] = myObject;
If you are doing an external file, then a generic .ashx handler is probably your best bet. It will be lighter than a whole aspx page and gives you pretty raw control over the output. Things you will want to be sure to do are to set the "Content-Type" response header to "text/javascript" (technically incorrect but most common MIME type) and for Internet Explorer which tends to not respect the Content-Type header, also set the "Content-Disposition" header to "inline;MyScriptName.js" so it knows the extension of the content.
Also, if these really are "constants" rather than runtime calculated data, you will want to set the "Expires" header to some future date to encourage the browser to cache the result. That will further reduce your server requests.
Edit: no DocType if you are creating JavaScript. DocType is only for markup.
You are using the MVC framework (your question is tagged as such) right? If so, you can create an action that returns a JavaScriptResult that will be executed on page when it loads:
public class JSController : Controller {
public ActionResult Headers() {
// create your variables here
return JavaScript("alert('hi');");
}
}
And then you can add it to your aspx/master page:
<script src="/JS/Headers" type="text/javascript"></script>

AJAX call to asp from bookmarklet

I'm trying to create a bookmarklet that will start off an AJAX call to an aspx page I've written.
The code tests out perfectly when I place the javascript in a static html page, but when I try and call it off from a bookmarklet, the code will just hang at the xmlHttp.open("GET", url, true) part.
The code of the bookmarklet is basically this (found on several examples on the web):
javascript:(function(){
var s,
d=document,
a=function(o){ d.body.appendChild(o) };
s=d.createElement('script');
s.type='text/javascript';
s.src='http://localhost/squirt/sq.js';
a(s)
})();
This adds the contents of sq.js (the ajax call + some other processing) to whatever page the browser is currently at, and then calls off the ajax to my aspx page.
I'm using ASP 2.0 (with VS2008) and IIS 7. So far I've just been testing it on my home network.
I assume there must be some sort of permissions issue with the ajax call from an outside page, since, like I said, everything works fine from a static page. Is this an IIS setting I need to change to allow the call, or am I doing something completely wrong?
The XMLHttpRequest object is subject to a Same Origin Policy.
This is why the script your bookmarklet is loading can't use an XHR to get data from your server unless it's embedded in a page from your server.
Script added by dynamically adding a script tag will work though, as you can tell - your bookmarklet can load script from a different origin.
So there's your answer. Don't use an XMLHttpRequest object: dynamically load your script in the same way the bookmarklet does.
This is how JSONP works (actually there's a bit more to JSONP but that's how it gets around the SOP)
Actually, why not just use JSONP
Injecting JavaScript code on the page still has the same permission issues as code that is there normally. You can not make an Ajax call to a different domain. So if you are calling localhost from example.com, it is not going to work.
You might want to look at returning JSON from your service and make JSON calls with a script tag.
Eric
The code you're using there is rather ugly, I would suggest using something like this that I built: http://sktrdie.org/getScript.js
It works like this:
getScript("http://anotherdomain.com/something", function(data) {
alert(data); // the request is complete
});
On the http://anotherdomain.com/something it would have to return something like this, given you're using PHP:
echo $_GET["jsonp"]."('Testing data, you can put anything in here');";
Be sure to read about JSONP.

Resources