开发者

Using relative url for window.location in child iframe

I'm having trouble with an iframe-related issue.

I have a document (parent) with an iframe (child). If the parent document calls a function in the child which changes the childs url using window.location to a relative url, that url becomes relative to the parent document. Let me illustrate.

Parent document: /test/parent.html

Child document: /projects/child.html

Child code:

function moveOn(){
    window.location.href = "newPage.html";
}

Parent code:

childDocument.contentWindow.moveOn();

Result: Child document tries to access /test/newPage.html instead of /projects/newPage.html

Is there any way of fixing this from the parent? I am in a situation where I c开发者_高级运维an't make changes in the child document code, only in the parent.

Thanks, Nik

Edit:

This is the actual code

In parent (/testr/testr.html):

testr.appHolder = $("childIframe"); 
testr.appHolder.src = "../projects/test.html"; 
testr.functionName = "moveOn"; 
testr.appHolder.contentWindow[testr.functionName]();

In child (/projects/test.html):

function moveOn(){ 
  window.location.href = "newPage.html"; 
}

This is the actual code

In parent (/testr/testr.html):

testr.appHolder = $("childIframe"); 
testr.appHolder.src = "../projects/test.html"; 
testr.functionName = "moveOn"; 
testr.appHolder.contentWindow[testr.functionName]();

In child (/projects/test.html):

function moveOn(){ 
  window.location.href = "newPage.html"; 
}


Finally, a full solution!

childDocument.contentWindow.setTimeout( childDocument.contentWindow.moveOn, 0 );

This asks the child document to execute the given function (moveOn) in zero milliseconds, and it is executed in the context of the child, even in Internet Exploder.

Booyah.


The HTML spec says:

When the [location property is set], the [browser] must resolve the argument, relative to the entry script's base URL, and if that is successful, must navigate the browsing context to the specified url.

One (hacky) way to get the behaviour you're after is to edit the pathname property of the location directly: effectively implementing the resolution of the relative url yourself.

In your case, you'd edit url "manually", using your child's existing url.

Dotdot solution

One "clever" way to do that is by appending a slash to turn the filename into a "directory", then erasing it with a "dotdot", followed by the relative url you want.

childDocument.contentWindow.location.pathname += "/../newPage.html";

(This code could also work within the child, but you mentioned you can't change the child code.)

Check out how this works.

The path starts as
/projects/child.html

Then you append to it, to get
/projects/child.html/../newPage.html

The browser resolves this to
/projects/newPage.html

Unfortunately, I imagine this won't be a perfect solution to you. Sounds like each of the "child" pages contain hard-coded links to the next page in the chain, which could be any page. Getting the "next" relative url for any given child page would require cooperation from the child.

If the child pages are named in a predictable way, and you know the total number of pages, and you know the base url of the child directory, you could have the parent control the child correctly. That's a lot of ifs.

Sidenote: I'm looking into a more elegant, robust solution. Fingers crossed.


Aha. I've found the baseURI property. Now we're talking.

The fundamental problem is that the baseURI of the document which is executing the code (the parent) is set to the parent's location. You want to set the baseURI of the page to be the same as the child's location before you change it's location, so that the relative url is resolved in the context of the child.

In short, it would be great to say:

var originalURI = document.baseURI; // store for setting back after the move
document.baseURI = childDocument.location; // setting parent's baseURI (NO EFFECT!)
childDocument.contentWindow.moveOn();
document.baseURI = originalURI; // restore original baseURI

Unfortunately, the baseURI property is read only. Fortunately, its value is taken from the <base> tag, which you can alter with script. You can get the functionality you want for a given child by setting the parent's base tag to refer to the childUrl:

<head>
<base href="/path/to/child.html" />
</head>

Unfortunately, this makes any relative links in your parent relative to the child, which is not what you want. We need to set this just after clicking the link, and set it back after the child has been told to moveOn().

If you include an empty <base /> tag in your parent's <head>, it will not effect the baseURI by default. So when you want to execute the moveOn() function in the child, you can say something like:

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

// adopt the child's location as baseURI
baseElement.href = childDocument.location; 

childDocument.contentWindow.moveOn();

// revert back to the parent's original baseURI
baseElement.removeAttribute("href"); 

(Hope this helps somehow - two years on... ha)

Edit:

Once again Internet Exploder is the party pooper. It seems that changes to any tag after load are not honoured. So unless you don't have to support IE (HA!), this solution won't work unless you're happy to hard code the child's location into the parent's tag. Again, those are big ifs.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜