使用<base>时,将锚链接引用当前页面

66

当我使用HTML <base> 标签为页面上所有相对链接定义基本URL时,锚点链接也会直接引用基本URL。有没有一种设置基本URL的方式,仍然允许锚点链接引用当前打开的页面?

例如,如果我在 http://example.com/foo/ 页面:


当前行为:

<base href="http://example.com/" />
<a href="bar/">bar</a> <!-- Links to "http://example.com/bar/" -->
<a href="#baz">baz</a> <!-- Links to "http://example.com/#baz" -->

期望的行为:

<base href="http://example.com/" />
<a href="bar/">bar</a> <!-- Links to "http://example.com/bar/" -->
<a href="#baz">baz</a> <!-- Links to "http://example.com/foo/#baz" -->

你正在使用服务器端编程语言吗?你可以在链接中动态内联当前请求的URI。另请参阅https://dev59.com/enI-5IYBdhLWcg3wYXL8#1889957 - BalusC
@BalusC 我不是,如果可能的话,我宁愿避免这种情况。 - Chris Down
好的,如果一切都已经是静态的了,那么就直接使用<a href="foo#baz"> - BalusC
1
我必须说这是错误的,您将基础设置为http://server/并告诉它导航到#baz,它将转到http://server/#baz。这就是相对URL的工作原理,以及使用<base>的效果,它会更改相对于哪个URL进行定位的基础部分。如果这不是您想要的结果,则您的链接不应该是相对的,或者它应该相对于基础的href(foo#baz)。 - Ruan Mendes
@JuanMendes 如果“foo”是一个未知的URL,那么这种方法就会失败。 - Abhi Beckert
@RuanMendes 我想大多数人都认为 href="#xyz" 总是指当前页面上的锚点,而不是完全不同的页面。毕竟 "#" 的整个意义就是跳转到文档的某个部分,而不是加载另一个页面。事实上,使用 <base href 可以使 "#" 自身(没有任何路径组件)在其他情况下不允许该行为的情况下加载完全不同的页面,我认为这不仅是技术上的缺陷,甚至是安全漏洞。 - Leslie Krause
13个回答

33

我在这个网站找到了一个解决方案:使用基本href和锚点,它不需要jQuery,并且这是可行的代码片段:

<base href="https://example.com/">

<a href="/test">/test</a>
<a href="javascript:;" onclick="document.location.hash='test';">Anchor</a>

或者没有内联JavaScript,像这样:

document.addEventListener('DOMContentLoaded', function(){
  var es = document.getElementsByTagName('a')
  for(var i=0; i<es.length; i++){
    es[i].addEventListener('click', function(e) {
      e.preventDefault()
      document.location.hash = e.target.getAttribute('href')
    })
  }
})

2
有很多原因为什么不好,其中一些在这里解释了:http://robertnyman.com/2008/11/20/why-inline-css-and-javascript-code-is-such-a-bad-thing/ - Stefano Saitta
15
使用内联JavaScript是完全有效的--它存在是有原因的。该文档中反对它的论点是牵强附会的。你应该基于整个内联代码构建大型项目吗?可能不应该。但你可以有意地使用内联代码来解决边缘情况或错误,这是完全可以的。这也是HTML规范的一部分。由于HTML文件大小,对内联JS实行全面禁令是无稽之谈。如果将相同的代码放在外部JS文件中,客户端仍然下载这些字节。 - Chris Baker
3
这个解决方案的兼容性较差。在禁用 JavaScript 的浏览器中(或者使用正则表达式的 HTML 解析器中),你的链接将无法正常工作。 - Joram van den Boezem
1
@ChrisBaker 自 2019 年和 2020 年以来,您真的应该避免使用内联 JS。如果您希望您的站点保证免受 XSS 攻击,您应该使用 CSP 标头来防止内联 JS 执行。请参阅 https://csper.io/blog/no-more-unsafe-inline - Ruan Mendes
1
@TutsInsider 无论是否采用此解决方法,这种情况都会发生。如果您单击某个内容,但链接已经加载到URL框中,则不会发生任何事情。 - Ruan Mendes
显示剩余3条评论

14

James Tomasino的答案基础上,这个方法略微更高效,解决了URL中双井号的错误和语法问题。

$(document).ready(function() {
    var pathname = window.location.href.split('#')[0];
    $('a[href^="#"]').each(function() {
        var $this = $(this),
            link = $this.attr('href');
        $this.attr('href', pathname + link);
    });
});

8

一点 jQuery 可能会帮助你解决这个问题。虽然 base href 起到了作用,但如果你想要以锚点(#)开头的链接完全相对于当前页面,请劫持所有链接,检查以#开头的 href 属性,并使用当前 URL 重建它们。

$(document).ready(function () {
    var pathname = window.location.href;
    $('a').each(function () {
       var link = $(this).attr('href');
       if (link.substr(0,1) == "#") {
           $(this).attr('href', pathname + link);
       }
    });
}

1
虽然在某些情况下这可能是可以的,但我正在制作的网站的受众可能会禁用JavaScript(或不可用)。由于在这种情况下唯一的后备选项将是故障,这似乎有点令人担忧。 - Chris Down
1
你最好的选择是从baseURL开始使用完整的相对路径,包括锚点,来编写所有链接的代码。 - James Tomasino

4

您也可以提供绝对URL:

<base href="https://example.com/">
<a href="/test#test">test</a>

与其这样

<a href="#test">test</a>

3

以下是基于jQuery的更简短版本,我在生产环境中使用并且效果很好。

$().ready(function() {
  $("a[href^='\#']").each(function() {
    this.href = location.href.split("#")[0] + '#' + this.href.substr(this.href.indexOf('#')+1);
  });
});

为什么要踩?虽然它可能不够优雅,但基本上与其他答案的方法相同,而这些答案远没有这个简洁,却已经得到了赞同。 - contendia
1
有人可能会因为它不能适应他们的情况而将其投票降级,而且不知道如何进行调整。我认为除了没有提到(假设需要)jQuery库之外,这里没有任何问题。 - Mavelo
@Robert 很好的建议。我更新了帖子以反映jQuery的要求。感谢您的反馈。 - contendia
2
不提及jQuery时建议使用jQuery是不被赞同的。 - Ruan Mendes
1
虽然这是一个学习网站,但程序员们应该知道如何将jQuery转换为纯JavaScript,尤其是因为几乎这个主题上的每个示例都依赖于一个或多个JS库。虽然库可以完成相同的任务,但它们可以简化从A点到B点的过程:P 我已经对他的解决方案进行了投票支持,因为在我看来它是完全有效的解决方案。 - Mavelo

2

恐怕没有办法解决这个问题,除非使用服务器端或浏览器端脚本。你可以尝试以下纯JavaScript(无jQuery)实现:

document.addEventListener("click", function(event) {
  var element = event.target;
  if (element.tagName.toLowerCase() == "a" && 
      element.getAttribute("href").indexOf("#") === 0) {
    element.href = location.href + element.getAttribute("href");
  }
});
<base href="https://example.com/">

<a href="/test">/test</a>
<a href="#test">#test</a>

与其他答案不同,它还适用于动态生成的(即使用JavaScript创建的)a元素。


@jor 对我来说它很好用。你使用的是哪个浏览器? - Michał Perłakowski
1
Firefox 47。我已经将哈希值连接起来了。还可以尝试打开一个已经包含哈希值的URL,并单击锚链接;这将把它附加到现有的哈希值中。 - jor

2
如果您使用PHP,可以使用以下函数生成锚链接:
function generateAnchorLink($anchor) {
  $currentURL = "//{$_SERVER['HTTP_HOST']}{$_SERVER['REQUEST_URI']}";
  $escaped = htmlspecialchars($currentURL, ENT_QUOTES, 'UTF-8');
  return $escaped . '#' . $anchor;
}

在代码中使用它的方式如下:

<a href="<?php echo generateAnchorLink("baz"); ?>">baz</a>

1
为了防止URL中出现多个#
document.addEventListener("click", function(event) {
  var element = event.target;
  if (element.tagName.toLowerCase() == "a" &&
    element.getAttribute("href").indexOf("#") === 0) {
    my_href = location.href + element.getAttribute("href");
    my_href = my_href.replace(/#+/g, '#');
    element.href = my_href;
  }
});

1
我的方法是搜索所有指向锚点的链接,并在链接前缀中加上文档URL。
这仅需要在初始页面加载时使用JavaScript,并保留浏览器功能,如在新标签页中打开链接。它也不依赖于jQuery等其他库。
document.addEventListener('DOMContentLoaded', function() {
  // Get the current URL, removing any fragment
  var documentUrl = document.location.href.replace(/#.*$/, '')

  // Iterate through all links
  var linkEls = document.getElementsByTagName('A')
  for (var linkIndex = 0; linkIndex < linkEls.length; linkIndex++) {
    var linkEl = linkEls[linkIndex]

    // Ignore links that don't begin with #
    if (!linkEl.getAttribute('href').match(/^#/)) {
      continue;
    }

    // Convert to an absolute URL
    linkEl.setAttribute('href', documentUrl + linkEl.getAttribute('href'))
  }
})

0

您可以在链接标签内使用一些 JavaScript 代码。

<span onclick="javascript:var mytarget=((document.location.href.indexOf('#')==-1)? document.location.href + '#destination_anchor' : document.location.href);document.location.href=mytarget;return false;" style="display:inline-block;border:1px solid;border-radius:0.3rem"
 >Text of link</span>

用户点击时它是如何工作的?

  1. 首先检查 URL 中是否已经存在锚点(#)。条件在问号(?)标志之前进行测试,以避免如果用户再次单击相同的链接,则无法重定向导致锚点在 URL 中被添加两次。
  2. 如果现有的 URL 中存在#符号,则将锚点附加到其中,并将结果保存在变量mytarget中。否则,保持页面URL不变。
  3. 最后,转到由mytarget变量存储的(修改或未更改的)URL。

除了使用 <span> 标记外,您还可以使用 <div> 或甚至是 <a> 标记。 建议避免使用 <a> 标记,以避免在 JavaScript 被禁用或不起作用时出现任何意外的重定向,并使用一些 CSS 样式模拟您的 <a> 标记的外观。

如果您仍然想使用<a>标签,不要忘记在JavaScript代码的末尾添加return false;并将href属性设置为<a onclick="这里是JavaScript代码;return false;" href="javascript:return false;">...</a>

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接