It's not stupid really.
As far as the .NET page parser is concerned, the script tag in the HTML:
is as meanful as a pile of jumbled text:
lkjoiflkjadkfjaiweraasdfk jaosdfj alsdfkj asdfasfj asdfj aslkfj asdfoi
ASP.NET doesn't care what you have there. It's all just literal HTML. You are directing the BROWSER to deal with the ~ character.
The <asp:hyperlink...> on the other hand is parsed into a server control. When ASP.NET renders that control it pumps the value of NavigateUrl thru the ResolveUrl() method that is found on the System.Web.UI.Page class. This translates the ~ into the full application page:
~/images/mypic.jpg -> /myapplication/images/mypic.jpg
The resolved URL is what ends up in the image tag:
<img src="/myapplication/images/mypic.jpg" />
If you want the script on an ASPX page, you don't need to use the ~ because the page is always in the same place. Just provide the right number of back reference directories to get you back up to the right directory the js
file lives in:
However, if you want to put a script tag on a user control you won't know where the HTML will be emitted (i.e. the user control could be used on a page at the application root or on a page 20 directories deep). Then you need to emit the script tag programmatically to provide a root relative file reference. I usually do that like this:
<asp:literal runat="server" id="litJSTag" />
This will resolve the js
file name to the full application path and give you the correct script tag in the literal control. (A literal control doesn't render any HTML other than the .Text property contents. Very handy for emiting various bits of script block of the like.)
You could also use the RegisterClientScriptBlock() method which eliminates the need for a literal control on the page.