logo

Solving a Simple Problem with Relative URLs

It seems like I've explained a solution to what must be a common problem enough times over the past couple of months to warrant answering it here (much easier to point to a link than to explain it over and over).

The Problem

Imagine you place an image on a Form using Passthru HTML. Something like this:

<img src="logo.jpg">

The problem is that when you create a new document using the form the image appears, but when you open the saved document in read mode it doesn't. It doesn't have to be an image. It can be any resource you load by URL, such as a CSS or JavaScript file, in which case the consequence would be much more obvious.

The Cause

This behaviour is down to the fact that the resource is being referenced as a relative URL. When the document is first being created the actual URL would be something like:

http://server.com/apps/help.nsf/ticket?OpenForm

So the browser will try and fetch the image from

http://server.com/apps/help.nsf/logo.jpg

This is the correct location and so the image loads. When the document is saved and re-opened the actual URL becomes something like:

http://server.com/apps/help.nsf/tickets/788A7878ED562132322EA21312?OpenDocument

And, so, the browser then tries to load the image from the following URL:

http://server.com/apps/help.nsf/tickets/logo.jpg

When reading documents we are, in effect, one "directory" deeper in to the site's structure and so relative URLs become broken.

The Solution

The quickest solution is to make the reference to the image/resource absolute. Like so:

<img src="/apps/help.nsf/logo.jpg">

However, if you're loading lots of images and/or resources it can become a drain on resources to compute the database path each time. Instead I'd recommend using the technique I use in all databases I create -- BASE href

I know this must seem a bit nooby to most of you, but you'd be surprised how often I see it come up as a problem.

Comments

  1. I would do this in a different way, keeping url relative.

    <img src="/<computed text>/image.jpg">

    where:

    computed text has formula @WebDBName

    image.jpg is an image resource in the NSF

    Regards.

    • avatar
    • Jake Howlett
    • Wed 17 Dec 2008 06:28 AM

    That's what I was suggesting Aecio. That doesn't keep it relative though. Did you mean absolute?

  2. @Aecio:

    The "problem" with that approach is that each of those Computed Text instances computes at runtime... making your given design element load that much slower (as Jake kinda mentions).

    Using the base href approach, the Domino HTTP task can do a really good job at caching the rendered element (within reason/depending on the element), and thus you're 1) delivering the page faster and 2) taxing the Domino server less.

    -Chris

  3. @Aecio

    I've found that that website rules and @WebDbName don't play well with one another.

    Explicitly defining a base href is, IMHO, the better choice.

    -Devin.

  4. Just be aware that if you use this technique and have anchor tags with the name attribute in your HTML content (or you use id's as anchor references) navigation links to anchors will not work as expected. I found that links to anchors attempt to open the base href URL and navigate the non-existant reference to the anchor instead of navigating to the anchor in the page that is displayed.

    • avatar
    • Flaz
    • Wed 17 Dec 2008 09:02 AM

    I think is always a good idea use the "absolute" solution, without the domain.

    I always use that, because for example a view could me loaded via web with:

    http://server/database/view?OpenView

    Or:

    http://server/database/view/?OpenView

    This make difference because if not absolute URL is used the above urls would have a different behaviour for a image "logo.gif":

    http://server/database/logo.gif

    And

    http://server/database/view/logo.gif

  5. I use base href as it's also a standard HTML approach, rather than a funky Domino thang.

  6. To add to this, I typically have my base href pull from a computed for display Text field that's hidden at the top of my Form Design Element, typically included via a Subform containing all common header information.

    Then, I use a single computed text instance:

    http:// + @GetHTTPHeader("HOST") + "/"

    Then, I use URL Substitution Rules to take all "/*" traffic and point it to "directory/subdirectory/sub-subdirectory/database.nsf/*".

    This is how the links on my site work -- much cleaner and it allows you to walk the URL without breaking anything, while still maintaining an ASP/hosting directory architecture in the backend.

    HTH,

    -Chris

  7. Here's my solution for the href parameter of the base element using Server_Name, Server_Port and Path_Info CGI fields in my common fields subform.

    "<base href=\"http" + @If(HTTPS = "ON"; "s"; "") + "://" + Server_Name + @If(Server_Port = "80"; ""; ":" + Server_Port) + "/" + @Left(Path_Info; @Length(@LeftBack(@LowerCase(Path_Info); ".nsf"))+4) + "/\"><!--[if IE]></base><![endif]-->" + @NewLine+

    You have to use http/https because not all browsers support the href starting with // in the base element. IE does, Firefox doesn't.

    I couldn't find a formula function that supports case insensitive searches so this is my solution to keep case. This is important with UNIX because it is case sensitive (.nsf and .NSF are not equal).

    Notice also the IE conditional comment to end the base element because of an IE bug that will make everything after the base element a child of the base element. This bug causes errors with dynamic scripting if you try to delete a script element from the head element it cause an error because the dynamically added script element is not under the head element, but the base element.

    Devin Olson,

    This formula takes care of any web rule issues with @WebDbName.

    John Marshall,

    Yes, the base element causes havoc with anchor links. You can solve this by inserting the Path_Info contents in front of the anchor link. If you are displaying a rich text field and you are sure that the user's browser supports javascript, you can use the following code onload or better yet DOMContentLoaded to change anchor links.

    // Name links because of the base element will not show the correct path. The will show

    //

    // http://server.domain.net/path/db.nsf/pages/#NamedLink

    //

    // Name links need to include the page name. fixNameLinks will add the page name to the

    // link.

    function fixNameLinks() {

    var sBase = document.getElementsByTagName("base")[0].href;

    var iLen = sBase.length;

    var els = document.getElementsByTagName("a");

    for(i = 0; i < els.length; i++) {

    var a = els[i];

    if(!a.href) {

    continue;

    }

    var iIndex = a.href.indexOf("#");

    if(iIndex == -1) {

    continue;

    }

    if(a.href.substr(0, iIndex) == sBase && iIndex == iLen) {

    a.href = location.pathname + a.hash;

    }

    }

    }

  8. In my javascript example above the href of the base element is "http://server.domain.net/path/db.nsf/pages/". The javascript looks for an anchor link that starts with the base then the # (hash mark). This is most likely wrong as it should point to base plus page's path. ie http://server.domain.net/path/db.nsf/pages/page_name#anchor_link

  9. Thanks for you comments on my comments!

    (@Jake, up there: Yes, I would mean absolute, not relative)

    For me, I don't like relative path much and I use absolute path always.

    Instead of the way I mentioned before, in forms I would add a field to calculate @webDBname just once in a compute for display field.

    If there is a concern about performance, there are some other rules - off-topic now - that *really* speed up page loading:

    1. move all css to top of page or head

    2. move all JS code to bottom of the page, before </body> ends.

    3. If there is no other way use, at most, two external JS in head.

Your Comments

Name:
E-mail:
(optional)
Website:
(optional)
Comment:


About This Page

Written by Jake Howlett on Wed 17 Dec 2008

Share This Page

# ( ) '

Comments

The most recent comments added:

Skip to the comments or add your own.

You can subscribe to an individual RSS feed of comments on this entry.

Let's Get Social


About This Website

CodeStore is all about web development. Concentrating on Lotus Domino, ASP.NET, Flex, SharePoint and all things internet.

Your host is Jake Howlett who runs his own web development company called Rockall Design and is always on the lookout for new and interesting work to do.

You can find me on Twitter and on Linked In.

Read more about this site »

More Content