Ticket #7822 (closed bug: worksforme)

Opened 3 years ago

Last modified 9 months ago

currentPage/isLocal doesn't take into account base href

Reported by: drewwells Owned by:
Priority: minor Milestone: 1.9.0
Component: ui.tabs Version: 1.8.16
Keywords: Cc:
Blocking: Blocked by:

Description

 https://github.com/jquery/jquery-ui/blob/master/ui/jquery.ui.tabs.js#L23

Currently, the URL of the page is being determined via location.href.replace, but this doesn't take into account a base href. As a result, local links ie '#hash' results in loading a foreign document into the page.

An accurate value is found in document.baseURI, but requires Dom lvl3. Padolsey has a decent solution, perhaps a simpler one can be employed.  http://james.padolsey.com/javascript/getting-a-fully-qualified-url/

Change History

comment:1 Changed 3 years ago by scott.gonzalez

  • Status changed from new to closed
  • Resolution set to worksforme

This does take into account the base href. The base only affects the anchor's href, not the current page's href. Please provide an example that actually breaks.

comment:2 Changed 3 years ago by drewwells

Notice the difference:  http://jsbin.com/uzecov/edit#javascript,html

"Relative URIs are resolved to full URIs using a base URI."  http://www.w3.org/TR/html401/types.html#type-uri

So "#ohjesusdontloadapageoverme" will return false from isLocal().

comment:3 follow-up: ↓ 4 Changed 3 years ago by scott.gonzalez

All that your test shows is that the base URI is different than the current page. That's pretty obvious, since that's what the base is for. #anything should return false from isLocal if you have a base that's different from the current page because it's a hash to a different page.

comment:4 in reply to: ↑ 3 Changed 3 years ago by drewwells

Yes that's what base href is used for running the page as if external resources are from a different uri. Since isLocal makes assumptions that location.href is used for building uri requests, it fails since location.href has nothing to do with loading resources.

Replying to scott.gonzalez:

All that your test shows is that the base URI is different than the current page. That's pretty obvious, since that's what the base is for. #anything should return false from isLocal if you have a base that's different from the current page because it's a hash to a different page.

comment:5 Changed 3 years ago by drewwells

Here is a gist showing the issue  https://gist.github.com/1322615

comment:6 Changed 3 years ago by scott.gonzalez

That gist properly loads the unicorn for the tab content. I'm not sure what you're trying to point out. location.href is definitely related to loading resources. if location.href != anchor.href, then it's not local.

Last edited 3 years ago by scott.gonzalez (previous) (diff)

comment:7 Changed 3 years ago by drewwells

The href is #cool-tab-1...

At least this should be flagged as 'wontfix' and put on the long list of edge cases for tabs. Or... do you disagree that #cool-tab-1 is not a local href?

Last edited 3 years ago by drewwells (previous) (diff)

comment:8 Changed 3 years ago by scott.gonzalez

You don't understand how base URIs work. #cool-tab-1 is not a local href, it's the equivalent of unicorn/#cool-tab-1. Remove the tabs and click the link.

comment:9 follow-up: ↓ 12 Changed 3 years ago by drewwells

What is this when in doubt, insult people? Go take it up with the specs not me, I don't write them.

All relative href tags like "#cool-tab-1" are resolved via the base element. The document's URI is resolved based off the location object. This is a hacky shortcut has confused the two and explains why my demo is broken.

 http://htmlhelp.com/reference/html40/head/base.html  http://www.w3.org/TR/html4/struct/links.html#h-12.4.1  http://www.whatwg.org/specs/web-apps/current-work/multipage/history.html#location

comment:10 follow-up: ↓ 11 Changed 3 years ago by scott.gonzalez

I don't know what you're trying to prove at this point. You're stating exactly the same thing I stated. "All relative href tags like "#cool-tab-1" are resolved via the base element." CORRECT! Which means that #cool-tab-1 is NOT LOCAL. It is resolved relative to the base, which is different from the current page. Did you even try what I said to see that the tabs plugin is doing exactly the same thing as what the browser does natively?

Last edited 3 years ago by scott.gonzalez (previous) (diff)

comment:11 in reply to: ↑ 10 Changed 3 years ago by drewwells

Yes the link is NOT LOCAL. Changing the base href on a page makes every link NOT LOCAL. I really don't care what the link is, I'm using inline content and this naive 'unobtrusive ajax' feature is half replacing it with remote content. Even if the links are left BLANK, it will still continue trying to fetch content from the base href.

I want to break my content into pieces that can be shown via clicking a tab just like the description says on jQuery UI site. Instead what I get is a widget that cannot have it's ajax features turned off, cannot recognise when content is already in a div on the page when base href has been changed, and can not have it's behavior overridden short of rewriting the proccessTabs method which even if I did would be broken in the current release and probably broken again in the next release.

comment:12 in reply to: ↑ 9 Changed 3 years ago by rdworth

Replying to drewwells:

What is this when in doubt, insult people?

Let's keep objective and cool-headed. "You don't understand ..." is not an insult[*], it's a statement of fact. Not a fact that you don't understand, but a fact that Scott doesn't believe you understand. Based on what you've said in the discussion, Scott believes you lack some level of understanding of the specification, the behavior of browsers or both. A gentler way to say it would be "I don't believe you understand..." or "I don't think your understanding is correct..." but we're taught in english class "don't start a sentence with 'I think' or 'I believe'; you're the one talking, if you make a statement it's reasonably understood that it's a statement of your own belief, etc. so just come out and say it"


[*] Ok, it might be an insult if it were "You don't understand anything about web development!! Idiot!!1" That's an insult. Scott's not saying that and I'm not saying that, just drawing a contrast.

comment:13 Changed 3 years ago by scott.gonzalez

Or you could use proper hrefs in your tabs... Use an absolute or relative URL that actually points at the correct place and it'll work fine. Tabs are doing exactly what they should. <a href="#cool-tab-1"> does not point to <div id="cool-tab-1"> in your page.

comment:14 Changed 2 years ago by willy

I know Tabs do what they should do. But I still want to ask what the good approach is when I meet the same situation. It is really hard to use "proper" hrefs as " http://api.jquery.com/add/" , " http://api.jquery.com/add" and " http://api.jquery.com/add?abc=123" are regarded different in isLocal() but linked to the same page. Or should I change the hrefs every time the page is loaded? Maybe it would be nice if there is an "isLocal" option.

Currently I add a line in isLocal() as workaround :

if ($(anchor).hasClass("local")) return true;

comment:15 Changed 2 years ago by scott.gonzalez

#8650 is a duplicate of this ticket.

comment:16 Changed 23 months ago by scott.gonzalez

#8768 is a duplicate of this ticket.

comment:17 Changed 22 months ago by scott.gonzalez

#8830 is a duplicate of this ticket.

comment:18 Changed 22 months ago by scott.gonzalez

#8831 is a duplicate of this ticket.

comment:19 Changed 22 months ago by gnarf

It seems non-trivial to fix this anywhere but inside isLocal somehow... I thought about it for a bit. I mean, using <base> really messes this up, but if you want to FORCE a tab to be local, there isn't a way to do that currently.

Just my $0.02 - I'm starting to sympathize with this bug.

comment:20 Changed 22 months ago by scott.gonzalez

I don't see how this is non-trivial to fix. The users who are running into this problem have to be dealing with <base> day in and day out. They have to user proper URLs on every single link they create, this is no exception. They *MUST* know how to properly generate URLs to link to pages within their app, otherwise they can't create any links at all.

Here's the trivial, dynamic PHP implementation: 'http://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'] . '#foo'.

It's also fairly trivial to fix this in JavaScript, but I won't provide sample code because that is the wrong place to be fixing this and should be highly discouraged. The behavior of links are clearly defined and consistent across all browsers. There is absolutely no reason people should be using incorrect URLs and then hacking around them in JavaScript.

Finally, it's important to note that "fixing" this would mean breaking the correct behavior for everyone who uses <base> properly. Keep in mind that this was fixed because people with proper URLs were running into the real bug that existed in the old code.

comment:21 Changed 22 months ago by mikesherov

Yeah, what's funny is that frameworks use the base tag specifically to avoid have to PHP to generate absolute urls. The real problem here is the existence of the base tag to begin with!

I too was starting to sympathize with those who run into this "bug", but then I remembered all the times I expected something to work and realized that I should understand the issue deeply first. Matching the spec and what browsers do is the right call here. If the base tag is a problem, then don't use it.

comment:22 Changed 22 months ago by scott.gonzalez

#8851 is a duplicate of this ticket.

comment:23 Changed 22 months ago by kasakka

The current implementation doesn't take into account loading tabs in something loaded with AJAX e.g. into a modal dialog. The location.href does not change then, but the REQUEST_URI does change. You would need to feed the AJAX loaded page the URL of the containing page so that the correct links (referring to the page where the modal dialog was opened) can be formed. Using REFERER for this is no good because in IE it doesn't work consistently.

Would adding a simple "forceLocal" option be that big of a deal? I don't see how making development more difficult and more inconsistent (especially since the documentation offers no help with the base tag issue) is going to help anyone. Having users move away from using base tag (and thus having to change a lot of stuff) is going to be more hassle than implementing a simple workaround.

comment:24 Changed 22 months ago by scott.gonzalez

#8855 is a duplicate of this ticket.

comment:25 follow-up: ↓ 26 Changed 22 months ago by scott.gonzalez

@kasakka Presumably everything you load via ajax is either broken or using absolute URLs. You've opted into using <base>, so you need to use it properly.

comment:26 in reply to: ↑ 25 Changed 22 months ago by kasakka

Replying to scott.gonzalez:

@kasakka Presumably everything you load via ajax is either broken or using absolute URLs. You've opted into using <base>, so you need to use it properly.

So let's say that my BASE tag href is " http://www.example.com/", I'm currently on page " http://www.example.com/pages/index", which has a button that opens a modal dialog where content is loaded via AJAX from " http://www.example.com/pages/stuff". Inside the pages/stuff page there are two tabs, with links that have HREF "#tab1" and "#tab2".

These evaluate to " http://www.example.com/#tab1" as per the BASE tag. What jQuery UI expects is " http://www.example.com/pages/index/#tab1" aka location.href + tab ID. I could make this work by manually changing pages/stuff's links' HREFs to "pages/index/#tab1" etc, but this would make the pages/stuff page unusable everywhere else but on pages/index. Should I want to load for example a control panel via AJAX on any page, those links would have to be changed to reflect that containing page.

The links are all relative to the base tag, but the AJAX loaded content when processed with PHP would have to know the URL of the containing page. With earlier jQ UI versions this was not necessary as it expected that simple href="#tab1" referred to a local tab so nothing broke.

And this is why there should be a "forceLocal" option. Correct me if I'm doing something completely wrong.

comment:27 Changed 22 months ago by scott.gonzalez

Correct me if I'm doing something completely wrong.

You're doing something completely wrong. Just read the previous comments. As I said before, you've opted into using <base>, so you need to use it properly.

Honestly, after a year of explaining this over and over, I'm getting tired of it. The previous comments explain what's going on. We're doing exactly the right thing and we're not going to add a flag to take broken URLs and treat them as if they're correct.

comment:28 Changed 22 months ago by kasakka

With many PHP frameworks, using base always as the root (as in my example) instead of the current page url saves a huge amount of aggravation with pretty much any url the framework generates. I still don't see how making tools harder to use is beneficial to anyone and I bet tickets for this same issue will keep popping up at least until the documentation is updated to explain the issue clearly.

Meanwhile I guess I'll just have to fork jQuery UI tabs and add in a workaround or move to using something else for tabs.

comment:29 Changed 21 months ago by scott.gonzalez

#8931 is a duplicate of this ticket.

comment:30 Changed 20 months ago by frame1986

I just have stumpled on this issue and have read the comments.

In my opinion, if jQuery UI deals with <div/> as anchor replacement you cannot say "we doing everything right, use base properly" because accessing a <div/> is not the common way a browser would handle this fragment.

A developer is expecting that when jQuery UI can handle with <div/> the script is checking that a id exists rather then just checking the href.

So if you give the feature a, you must also implement feature b.

comment:31 Changed 20 months ago by scott.gonzalez

@frame1986 I have no idea what you're talking about. Where do we use divs as replacements for anchors? If you're talking about the fact that the hash points to a div, that's how HTML works. Hashes don't point to anchor elements, they point to elements based on their name or id.

Last edited 20 months ago by scott.gonzalez (previous) (diff)

comment:32 Changed 20 months ago by tj.vantoll

#9022 is a duplicate of this ticket.

comment:33 Changed 20 months ago by henk verouden

I debugged 3 days after changing 1.8.3 to 1.10 en jquery to 1.9 and did finally find out that this is the problem, my use of the basetag. But it seems to be it for over 15 months now in the community. This is unbelievable and seen the discussion it looks that it will not be fixed in the next 15 months. If I must rewrite my framework to not use the basetag, it takes lots and lots of work. That is not an option. The only option is to hack the code or stop with jquery ui. This was more debugging time than writing a small tab set by myself.

I hate hacking, than i can't use the google cdn anymore. Could you please add an workaround? The isLocal class is workable for me.

comment:34 Changed 19 months ago by Volfin

I too have to say I'm rather shocked at the arrogance the 'devs' are showing here. They are the ones that don't seem to understand how a <base href> works. It's just that, the *base* of all URLs on the given page. And that's all, there's nothing else special about it.

if you can resolve  http://www.angrydev.com/pages/#tab-1 as being local to  http://www.angrydev.com/pages/

There is No reason you shouldn't be able to resolve /#tab-1 as being local to / (with a base href of " http://www.angrydev.com/pages/")

it's the same damn situation. But you're refusing to support it just because it's using a Base HREF. and that is just not even sad, it's ridiculous.

You had no problem doing it up until 1.9.0. So why the hell can't you do it now? Especially when so many have shown it's needed, and so many have given you the code to do it?

comment:35 Changed 19 months ago by Volfin

I will also add that I discovered a workaround that is simpler than any I've seen so far presented.

$("#tabs").tabs({beforeLoad: function( event, ui ) { event.preventDefault(); return; } });

calling with preventDefault() will kill any attempts to load remotely. But it's an all or nothing proposition, unless you add more logic.

comment:36 Changed 19 months ago by scott.gonzalez

#9092 is a duplicate of this ticket.

comment:37 Changed 19 months ago by tj.vantoll

Due to the large number of duplicate submissions I felt the need to write up a guide on how to use the base tag and the tabs widget together properly -  http://tjvantoll.com/2013/02/17/using-jquery-ui-tabs-with-the-base-tag/. I discuss why the tabs widget does what it does, how to fix your applications correctly, and how to add a JS hack to get things working immediately.

comment:38 Changed 18 months ago by scott.gonzalez

#9182 is a duplicate of this ticket.

comment:39 Changed 16 months ago by scott.gonzalez

#9288 is a duplicate of this ticket.

comment:40 follow-up: ↓ 41 Changed 13 months ago by uvahiker

Here is my setup: using jQuery 1.8.2 and jQuery UI 1.9.0 with HTML pages running in WebKit inside of a QT application. I am using tabs on one of my pages with local links (see jQueryUI demo). It worked fine in my development environment and it did NOT work in a deployed/installed environment. When it didn't work, the entire page loaded into the tabs container. All of the necessary files were present. Again, it worked in one case and not the other. What is the difference? The installed environment was under the "Program Files (x86)" directory which contains spaces. That was the difference. Period. (Also, I don't have a <base> tag.)

Here is my fix: In jQuery UI 1.9.0, the isLocal() function is defined above the "$.widget( "ui.tabs", {" line. Replace spaces with "%20":

function isLocal( anchor ) {
    anchor = anchor.cloneNode( false );
    return anchor.hash.length > 1 &&
		anchor.href.replace( rhash, "" ) === location.href.replace( rhash, "" ).replace( /\s/g, "%20" );
}

The addition is the ".replace( /\s/g, "%20" )" on the end.

Credit to: user "MV." on StackOverflow:  http://stackoverflow.com/questions/13837304/jquery-ui-non-ajax-tab-loading-whole-website-into-itself

I would kindly request that this fix be added to the jQuery baseline since it is simple.

Last edited 13 months ago by uvahiker (previous) (diff)

comment:41 in reply to: ↑ 40 ; follow-ups: ↓ 42 ↓ 43 Changed 13 months ago by tj.vantoll

Replying to uvahiker:

Here is my setup: using jQuery 1.8.2 and jQuery UI 1.9.0 with HTML pages running in WebKit inside of a QT application. I am using tabs on one of my pages with local links (see jQueryUI demo). It worked fine in my development environment and it did NOT work in a deployed/installed environment. When it didn't work, the entire page loaded into the tabs container. All of the necessary files were present. Again, it worked in one case and not the other. What is the difference? The installed environment was under the "Program Files (x86)" directory which contains spaces. That was the difference. Period. (Also, I don't have a <base> tag.)

Here is my fix: In jQuery UI 1.9.0, the isLocal() function is defined above the "$.widget( "ui.tabs", {" line. Replace spaces with "%20":

function isLocal( anchor ) {
    anchor = anchor.cloneNode( false );
    return anchor.hash.length > 1 &&
		anchor.href.replace( rhash, "" ) === location.href.replace( rhash, "" ).replace( /\s/g, "%20" );
}

The addition is the ".replace( /\s/g, "%20" )" on the end.

Credit to: user "MV." on StackOverflow:  http://stackoverflow.com/questions/13837304/jquery-ui-non-ajax-tab-loading-whole-website-into-itself

I would kindly request that this fix be added to the jQuery baseline since it is simple.

This was fixed in 1.10, see #8896.

comment:42 in reply to: ↑ 41 Changed 13 months ago by uvahiker

Replying to tj.vantoll:

Replying to uvahiker:

Here is my setup: using jQuery 1.8.2 and jQuery UI 1.9.0 with HTML pages running in WebKit inside of a QT application. I am using tabs on one of my pages with local links (see jQueryUI demo). It worked fine in my development environment and it did NOT work in a deployed/installed environment. When it didn't work, the entire page loaded into the tabs container. All of the necessary files were present. Again, it worked in one case and not the other. What is the difference? The installed environment was under the "Program Files (x86)" directory which contains spaces. That was the difference. Period. (Also, I don't have a <base> tag.)

Here is my fix: In jQuery UI 1.9.0, the isLocal() function is defined above the "$.widget( "ui.tabs", {" line. Replace spaces with "%20":

function isLocal( anchor ) {
    anchor = anchor.cloneNode( false );
    return anchor.hash.length > 1 &&
		anchor.href.replace( rhash, "" ) === location.href.replace( rhash, "" ).replace( /\s/g, "%20" );
}

The addition is the ".replace( /\s/g, "%20" )" on the end.

Credit to: user "MV." on StackOverflow:  http://stackoverflow.com/questions/13837304/jquery-ui-non-ajax-tab-loading-whole-website-into-itself

I would kindly request that this fix be added to the jQuery baseline since it is simple.

This was fixed in 1.10, see #8896.

Thank you for the response. I haven't tested it with 1.10 yet (I'll upgrade after our release), but I'm sure it works if another bug was filed. I just wanted to provide more info for other developers running into this issue.

comment:43 in reply to: ↑ 41 Changed 12 months ago by ladamson

Replying to tj.vantoll:

This was fixed in 1.10, see #8896.

A note for others: that "fixed" message applies to a different issue reported by uvahiker in this thread, not the one this bug report was originally about.

My take on the original issue is:

  • The devs are correct that isLocal() called for #hash when a BASE element is present should return False.
  • However, the users are not actually asking to change that behavior. What the users actually want is a way to tell Tabs: "treat the href value as an ID, even if it looks like a URL". That seems reasonable, does not change the correct default behavior, and does not go against any specifications. It's just telling Tabs how you'd like it to behave on a specific page.

Given that, this should not be a bug report, but should be a feature request for a "treatAs" option with the values: "auto", "remote", "local", with "auto" being the default and having the current behavior.

comment:44 Changed 12 months ago by sneps

please add the "treatAs" option... or a "noBaseTag" option... that would be very nice for everyone and you can keep your basetag standards when the option is not set...

comment:45 Changed 12 months ago by scott.gonzalez

#9587 is a duplicate of this ticket.

comment:46 follow-up: ↓ 47 Changed 11 months ago by cwingrav

This took me a long time to figure out too. Let me explain, as I think the devs and users are ALL correct, in different ways. UI-DEVS: "This links to the content AS INTENDED. 'nuff said." UI-USERS: "This does not link to the div. Problem."

Problem: I think the problem is that the UI devs see the anchor tags as linking to both on-page and off-page content. A feature since it can load all kinds of things. The users however can't figure out why the href, which LOOKS like like a jQuery selector, isn't going to the div the selector of the anchor points too. Seems like the dual use of in-page links coming from both 'name' and 'id' attributes is what is screwing this up. That, and not even realizing that tabs can grab remote content.

Proposed Solution: I think merely changing the example code to demonstrate: a local div, a div using remote content and a fully qualified URL to a local div with a bit of text mentioning the base URL issue. For instance, I didn't even realize until this issue appeared, that I can load remote content in a tab.

Comments?

comment:47 in reply to: ↑ 46 ; follow-up: ↓ 48 Changed 11 months ago by scott.gonzalez

Replying to cwingrav:

Proposed Solution: I think merely changing the example code to demonstrate: a local div, a div using remote content and a fully qualified URL to a local div with a bit of text mentioning the base URL issue. For instance, I didn't even realize until this issue appeared, that I can load remote content in a tab.

We already have this:  http://jqueryui.com/tabs/#ajax

And the problem is that most people using <base> don't want to take the time to write the correct URL. You'll notice that everyone else complaining in this ticket isn't complaining about a lack of documentation (there is no such lack), they're all asking for a way to change what the anchor is referencing.

comment:48 in reply to: ↑ 47 Changed 11 months ago by cwingrav

Thanks for the quick response. I appreciate it.

Replying to scott.gonzalez:

Replying to cwingrav:

Proposed Solution: I think merely changing the example code to demonstrate: a local div, a div using remote content and a fully qualified URL to a local div with a bit of text mentioning the base URL issue. For instance, I didn't even realize until this issue appeared, that I can load remote content in a tab.

We already have this:  http://jqueryui.com/tabs/#ajax

I never noticed that example since it's not on the 1st page, but under the ajax link... one of 8 links on the right, and since I'm not using Ajax with my tab, I never clicked it. It would be more helpful if one of the tabs on the 1st page demonstrated this loading of off-page content. It's not a bug at all. I realize that. It's a misunderstanding of how it works. That's what's tripping people like me up.

And the problem is that most people using <base> don't want to take the time to write the correct URL. You'll notice that everyone else complaining in this ticket isn't complaining about a lack of documentation (there is no such lack), they're all asking for a way to change what the anchor is referencing.

Hmm, maybe, but not me. For me, I thought it only loaded content on-page and the href of the tab was a link to an id of the div that was selected by the tab (and why I thought it was odd that you all did it this way... which you didn't I now know). I didn't know it had anything to do with a URL at all, even though it was an href. This was a misunderstanding on my part, because the href in the example code looked like a selector. But, I think if a single example in the front page demonstrated this, it would have saved me about 45 minutes.

Cheers and keep up the good work.

comment:49 Changed 9 months ago by scott.gonzalez

#9712 is a duplicate of this ticket.

Note: See TracTickets for help on using tickets.