-1

A website has multiple pages with imported HTML from another page with id tags that need to be simplified.

It currently looks like this.

<h2> 
    <a id="user-content-test1" href="https://www.example.com">
        Anything
    </a>
</h2>
<h2> 
    <a id="user-content-best2" href="https://www.example.com">
        Anything
    </a>
</h2>
<h2> 
    <a id="user-content-nest3" href="https://www.example.com">
        Anything
    </a>
</h2>
<h2> 
    <a id="user-content-rest4" href="https://www.example.com">
        Anything
    </a>
</h2>

There are anchor links that point to all of these ids, but these links do not include the "user-content-" part. They look like this Link to anchor. They do NOT look like this Link to anchor. There are too many of these id's to change manually.

How can I change the value of all the id tags from id="user-content-test1 to just id="test1 using jQuery or pure JS? The desired result should be:

<h2> 
    <a id="test1" href="https://www.example.com">
        Anything
    </a>
</h2>
<h2> 
    <a id="best2" href="https://www.example.com">
        Anything
    </a>
</h2>
<h2> 
    <a id="nest3" href="https://www.example.com">
        Anything
    </a>
</h2>
<h2> 
    <a id="rest4" href="https://www.example.com">
        Anything
    </a>
</h2>

I have searched all over stackoverflow and google but I only find how to replace strings, not IDs. I have tried both of these scripts with no results.

<script>
$(document).ready(function(){
let result = 'user-content-'.replaceAll(/\+/g, ' ');
});
</script>
<script>
$(document).ready(function(){
var find = 'user-content-';
var re = new RegExp(find, 'g');

str = str.replace(re, '');});
</script>
edvard
  • 13
  • 3
  • “*How can I change the value of all the id tags*” - why do you need to, what problem are you trying to solve? – David Thomas Mar 20 '21 at 02:18
  • There are anchor links that point to all of these id's, but these links do not include the "user-content-" part. They look like this Link to anchor. They do NOT look like this Link to anchor. There are too many of these id's to change manually. – edvard Mar 20 '21 at 02:35

3 Answers3

1

You can very quickly use jQuery to match all <a> tags in <h2> tags, then replace all their ids. Your problem can be diluted into smaller steps:

  • Find a way to match your desired elements
  • Find a way to get the ID attribute of those elements
  • Manipulate the string inside that ID attribute

The first two can be done with jQuery or Pure JS, and the third can just be done with simple string manipulation, like with String.slice() as "user-content-" is of fixed length anyway.

Voltaire
  • 93
  • 2
  • 9
  • Thanks for the answer. I can only find how to replace specific IDs like this `document.getElementById('user-content-test1').setAttribute('id', 'test1');` I am looking for a way to find all ID's that contains **user-content-** and then remove that part from the ID value. – edvard Mar 20 '21 at 02:51
0

use the selector $("h2 a[id^=user-content]") -> that means select all id beginning by user-content

$(document).ready(function() {
  $("h2 a[id^=user-content]").each((i, e) => {

    let id = $(e).attr("id");
    $(e).attr("id", id.replace("user-content-", ""));
  });
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<h2>
  <a id="user-content-test1" href="https://www.example.com">
        Anything
    </a>
</h2>
<h2>
  <a id="user-content-best2" href="https://www.example.com">
        Anything
    </a>
</h2>
<h2>
  <a id="user-content-nest3" href="https://www.example.com">
        Anything
    </a>
</h2>
<h2>
  <a id="user-content-rest4" href="https://www.example.com">
        Anything
    </a>
</h2>
Frenchy
  • 16,386
  • 3
  • 16
  • 39
0

As the problem is that you have anchor elsewhere in the document that link to these particular elements there are three ways I can think of to solve the problem:

  1. change the id of these elements so that they match the fragment-identifier of the <a> elements linking to them (as your question explicitly asked), or
  2. change the href of the elements which link to them; this means any styles targetting these elements wouldn't require their selectors to be adjusted.

In each of these potential solutions there's a relatively simple JavaScript, and jQuery, solution.

All approaches will be using the following HTML and CSS:

:root {
  --color: #000f;
  --backgroundColor: #fff;
}

*,
::before,
::after {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
  font-family: sans-serif;
  line-height: 1.5;
}

nav ul {
  display: flex;
  justify-content: space-around;
  list-style-type: none;
}

nav a:is(:link, :visited) {
  color: var(--color);
  background-color: var(--backgroundColor);
}

nav a:is(:hover, :active, :focus) {
  color: var(--backgroundColor);
  background-color: var(--color);
}

h2 {
  margin-block: 3em;
}

h2 a:is(:link, :visited) {
  background: linear-gradient(90deg, lime, #ffff);
  display: block;
  color: var(--color);
  text-decoration: none;
}

h2 a:is(:hover, :active, :focus) {
  text-decoration: underline;
  text-decoration-thickness: 3px;
}

h2 a:target {
  background: linear-gradient(90deg, #f90, #ffff);
}

h2 a::after {
  content: ' (#' attr(id) ').';
}
<nav id="toc">
  <ul>
    <li><a href="#test1">Link to "test1"</a></li>
    <li><a href="#best2">Link to "best2"</a></li>
    <li><a href="#nest3">Link to "nest3"</a></li>
    <li><a href="#rest4">Link to "rest4"</a></li>
  </ul>
</nav>
<h2>
  <a id="user-content-test1" href="https://www.example.com">
    Anything
  </a>
</h2>
<h2>
  <a id="user-content-best2" href="https://www.example.com">
    Anything
  </a>
</h2>
<h2>
  <a id="user-content-nest3" href="https://www.example.com">
    Anything
  </a>
</h2>
<h2>
  <a id="user-content-rest4" href="https://www.example.com">
    Anything
  </a>
</h2>

So, to directly answer your question: how can you change the id of the user-content-* elements to remove the user-content- part:

First, with plain JavaScript:

// utility function written by: Michał Perłakowski
// (https://stackoverflow.com/users/3853934/)
// taken from:
// https://stackoverflow.com/a/39977764/82548
// used because the behaviour of Object.assign() doesn't
// work well in merging objects with unspecifed values/keys:
const assign = (target, ...sources) =>
  Object.assign(target, ...sources.map(x =>
    Object.entries(x)
    .filter(([key, value]) => value !== undefined)
    .reduce((obj, [key, value]) => (obj[key] = value, obj), {})
  ));

// defining a named function, which accepts an Object of user-
// defined options in order to change the default behaviour of
// the function; if no Object is passed in the function
// sets the opts variable to an empty Object:
function removeTextFromAttribute(opts = {}) {

  // these are the default settings for the function:
  let defaults = {
      // String, accepts a CSS selector to select the
      // relevant elements:
      elements: 'a',
      // String, accepts the attribute-name from which
      // you wish to remove characters:
      attribute: 'id',
      // String, the string you wish to remove from
      // those attributes:
      remove: 'user-content-',
      // Boolean, does the attribute start with the String
      // you wish to remove:
      startsWith: false,
      // Boolean, does the attribute end with the String
      // you wish to remove:
      endsWith: false,
    },

    // here we use the utility function (cited above) in
    // order to compensate for the behaviour of the
    // native Object.assign() functionality (see the
    // question related to the linked answer). It
    // seems that objects later in the argument-list of
    // the function overwrite declared keys of previous
    // Objects, and we want the user to overwrite the
    // defaults; so remember to put the user-defined
    // opts Object last in order that it overwrites
    // the defaults:
    settings = assign({}, defaults, opts);

  // using Object destructuring assignment to assign the
  // resulting options to named variables (mainly to avoid
  // having to type settings.elements, settings.attribute...):
  const {
    elements,
    attribute,
    remove,
    startsWith,
    endsWith
  } = settings;

  // declaring a variable, without initialising it:
  let selector;

  // if the user sets both the startsWith and EndsWith value
  // to true, then the attribute-value to be changed may either
  // be equal to the provided String or it may both begin and
  // start with the provided string; to simplify selection in
  // these cases we set the selector variable to '*=' which
  // selects an element with the attribute, and an attribute-
  // value appears within the String at least once. Otherwise
  // if the startsWith and endsWith are both false this implies
  // that the remove String appears within the attribute-value
  // somewhere, so we also use the '*=' selector:
  if ((startsWith && endsWith) || (!startsWith && !endsWith)) {
    selector = '*=';

    // if startsWith is true and endsWith is not, we use the
    // attribute-value-starts with selector:
  } else if (startsWith && !endsWith) {
    selector = '^=';
    // finally if startsWith is falsey, and endsWith is true:
    // we use the attribute-value-ends-with selector:
  } else if (!startsWith && endsWith) {
    selector = '$=';
  }

  // here we use document.querySelectorAll to select all
  // elements that match the created-selector, which takes
  // the form of: 'Element[attribute<selector>"StringToRemove"]',
  // for example: 'a[id^="user-content-"]'
  const haystack = document.querySelectorAll(
    // we use a template literal string to interpolate the
    // various variables into the resulting String, this is
    // in order to avoid having to concatenate Strings and
    // variables:
    `${elements}[${attribute}${selector}"${remove}"]`
  );

  // we use NodeList.prototype.forEach() to iterate over the 
  // list of Nodes returned:
  haystack.forEach(
    // we use an Arrow function, since we have no need to use
    // 'this'; 'el' is a reference to the current Node of the
    // NodeList over which we're iterating:
    (el) => {
      // we use Element.getAttribute to retrieve the attribute
      // to be modified:
      let attr = el.getAttribute(attribute);

      -
    });
}

// calling the named function, in this case with all the default
// settings and passing no arguments as the defaults are set to
// your specified functionality:
removeTextFromAttribute();
:root {
  --color: #000f;
  --backgroundColor: #fff;
}

*,
::before,
::after {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
  font-family: sans-serif;
  line-height: 1.5;
}

nav ul {
  display: flex;
  justify-content: space-around;
  list-style-type: none;
}

nav a:is(:link, :visited) {
  color: var(--color);
  background-color: var(--backgroundColor);
}

nav a:is(:hover, :active, :focus) {
  color: var(--backgroundColor);
  background-color: var(--color);
}

h2 {
  margin-block: 3em;
}

h2 a:is(:link, :visited) {
  background: linear-gradient(90deg, lime, #ffff);
  display: block;
  color: var(--color);
  text-decoration: none;
}

h2 a:is(:hover, :active, :focus) {
  text-decoration: underline;
  text-decoration-thickness: 3px;
}

h2 a:target {
  background: linear-gradient(90deg, #f90, #ffff);
}

h2 a::after {
  content: ' (#' attr(id) ').';
}
<nav id="toc">
  <ul>
    <li><a href="#test1">Link to "test1"</a></li>
    <li><a href="#best2">Link to "best2"</a></li>
    <li><a href="#nest3">Link to "nest3"</a></li>
    <li><a href="#rest4">Link to "rest4"</a></li>
  </ul>
</nav>
<h2>
  <a id="user-content-test1" href="https://www.example.com">
    Anything
  </a>
</h2>
<h2>
  <a id="user-content-best2" href="https://www.example.com">
    Anything
  </a>
</h2>
<h2>
  <a id="user-content-nest3" href="https://www.example.com">
    Anything
  </a>
</h2>
<h2>
  <a id="user-content-rest4" href="https://www.example.com">
    Anything
  </a>
</h2>

And with jQuery:

// here we're creating a jQuery plugin using the IIFE - Immediately-
// Invoked Function Expression - approach:
(function($) {

  // defining the name of the plugin, and passing in the opts argument:
  $.fn.removeTextFromAttribute = function(opts) {
    // defining the defaults (as above), although we're not
    // defining the element-types upon which we wish to work
    // as jQuery passes that collection to the plugin:
    let defaults = {
        attribute: 'id',
        remove: 'user-content-',
        startsWith: false,
        endsWith: false,
      },
      // a far easier means of combining the user-defined
      // properties with the default properties (bear in
      // mind this is simple because the library has taken
      // care of it elsewhere, so it's more concise for
      // yourself but obviously including the whole library
      // has its own cost/benefit analysis to be undertaken):
      settings = $.extend(defaults, opts);

    // again using destructuring assignment:
    const {
      attribute,
      remove,
      startsWith,
      endsWith
    } = settings;

    // declaring, but not initialising, the 'selector' variable:
    let selector;

    if ((startsWith && endsWith) || (!startsWith && !endsWith)) {
      selector = '*=';
    } else if (startsWith && !endsWith) {
      selector = '^=';
    } else if (!startsWith && endsWith) {
      selector = '$=';
    }

    // using a template String to create the attribute-selector:
    selectorString = `[${attribute}${selector}"${remove}"]`

    // here we return the results of these chained method calls,
    // first we use the filter() method:
    return this.filter(function(index, elem) {

      // we use a native JavaScript Element.matches method
      // to establish whether the current Node of the jQuery
      // collection matches the selector string we've
      // created above; if yes (Boolean true) that node is
      // retained in the collection, if not (Boolean false)
      // the current Node is discarded from the collection:
      return this.matches(selectorString);
      
      // next we use the attr() method as a setter, in order
      // to modify the named attribute, and use the 
      // anonymous callback function to modify each attribute
      // in turn:
    }).attr(attribute, function(i, attr) {

      // this is exactly the same as the above - Plain
      // JavaScript - version:
      if (attr === remove) {
        this.removeAttribute(attribute);
      } else if (attr.includes(remove)) {
        this.setAttribute(
          attribute,
          attr.replace(new RegExp(remove, 'g'), ''));
      }
    });
  }

  // passing jQuery into the function:
}(jQuery));

// note that here we selected all <a> elements, this forms
// the collection passed to the jQuery plugin and is why
// we didn't need to specify the attribute-type within the
// plugin:
$('a').removeTextFromAttribute();
:root {
  --color: #000f;
  --backgroundColor: #fff;
}

*,
::before,
::after {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
  font-family: sans-serif;
  line-height: 1.5;
}

nav ul {
  display: flex;
  justify-content: space-around;
  list-style-type: none;
}

nav a:is(:link, :visited) {
  color: var(--color);
  background-color: var(--backgroundColor);
}

nav a:is(:hover, :active, :focus) {
  color: var(--backgroundColor);
  background-color: var(--color);
}

h2 {
  margin-block: 3em;
}

h2 a:is(:link, :visited) {
  background: linear-gradient(90deg, lime, #ffff);
  display: block;
  color: var(--color);
  text-decoration: none;
}

h2 a:is(:hover, :active, :focus) {
  text-decoration: underline;
  text-decoration-thickness: 3px;
}

h2 a:target {
  background: linear-gradient(90deg, #f90, #ffff);
}

h2 a::after {
  content: ' (#' attr(id) ').';
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<nav id="toc">
  <ul>
    <li><a href="#test1">Link to "test1"</a></li>
    <li><a href="#best2">Link to "best2"</a></li>
    <li><a href="#nest3">Link to "nest3"</a></li>
    <li><a href="#rest4">Link to "rest4"</a></li>
  </ul>
</nav>
<h2>
  <a id="user-content-test1" href="https://www.example.com">
    Anything
  </a>
</h2>
<h2>
  <a id="user-content-best2" href="https://www.example.com">
    Anything
  </a>
</h2>
<h2>
  <a id="user-content-nest3" href="https://www.example.com">
    Anything
  </a>
</h2>
<h2>
  <a id="user-content-rest4" href="https://www.example.com">
    Anything
  </a>
</h2>

The second approach — updating the href property of the <a> elements that link to these <a> elements is a very similar approach as used above, but obviously we're inserting/updating attribute-values rather than removing from them. So this will take a new function, which is below:

First, with plain JavaScript:

// utility function written by: Michał Perłakowski
// (https://stackoverflow.com/users/3853934/)
// taken from:
// https://stackoverflow.com/a/39977764/82548
// used because the behaviour of Object.assign() doesn't
// work well in merging objects with unspecifed values/keys:
const assign = (target, ...sources) =>
  Object.assign(target, ...sources.map(x =>
    Object.entries(x)
    .filter(([key, value]) => value !== undefined)
    .reduce((obj, [key, value]) => (obj[key] = value, obj), {})
  ));

// new named function, set up in the same way as above,
// albeit with new arguments:
function insertTextIntoAttribute(opts = {}) {
  let defaults = {
      attribute: 'id',
      elements: 'a',
      // Boolean, do you wish to insert the new String
      // at the end of the current value?
      endWith: false,
      // String, the string you wish to insert:
      insert: 'user-content-',
      // Boolean, do you wish to insert the new String
      // at the start of the current value?
      startWith: true,
    },
    settings = assign({}, defaults, opts);

  const {
    elements,
    attribute,
    insert,
    startWith,
    endWith
  } = settings,
  // using a template literal to create a simple selector
  // to find the elements that match your requirements:
  selectorString = `${elements}`;

  // using document.querySelectorAll() to retrieve a 
  // NodeList of elements that match the selector
  // passed to the function:
  const haystack = document.querySelectorAll(
    selectorString
  );

  // NodeList.prototype.forEach() to iterate over the
  // returned NodeList:
  haystack.forEach(
    (el) => {
      // we retrieve the current attribute-value of the
      // relevant element:
      let currentValue = el.getAttribute(attribute),
        // because a hash requires some special consideration
        // (the '#' character has to be at the beginning) we
        // initialise this variable to false:
        isHash = false;

      // we use Element.matches to see if the current element
      // of the NodeList is an <a> element (we could have instead
      // used el.tagName === 'A') but Element.matches is
      // more concise, easier to read and doesn't require a
      // comparison), it is we then check if the current attribute-
      // value matches the <a> element's hash:
      if (el.matches('a') && currentValue === el.hash) {
        // if it does we then update the isHash variable to true:
        isHash = true;
      }

      // here we use Element.setAttribute() to update the named
      // attribute (first argument) to a new value:
      el.setAttribute(attribute,

        // this is perhaps a little confusing to read, as we're
        // taking advantage of Template strings' ability to
        // interpolate a variable into the String, and we're
        // using conditional operators to do so. In order:
        // 1. ${isHash ? '#' : ''}
        // we test isHash; if true/truthy
        // the expression returns the '#' character, if false/falsey
        // the expression returns the empty String ''.
        // 2. ${startWith ? insert : ''}
        // we test startWith; if true/truthy the expression returns
        // the 'insert' variable's value, otherwise if startWith is
        // false/falsey the expression returns the empty-string.
        // 3. ${isHash ? currentValue.replace('#','')
        // here we again test the isHash variable, and if true/truthy
        // the expression returns the result of calling
        // String.prototype.replace() on the current attribute-value
        // of the element; if isHash is false/falsey then it simply
        // returns the current attribute-value.
        // 4. ${endWith ? insert : ''}
        // this is exactly the same as the earlier assessment for
        // the startWith variable, if true/truthy we return the
        // content of the insert variable, otherwise if false/falsey
        // we return an empty String:
        `${isHash ? '#' : ''}${startWith ? insert : ''}${isHash ? currentValue.replace('#','') : currentValue}${endWith ? insert : ''}`
      );
    });
}

// here we call the function, specifying our
// options:
insertTextIntoAttribute({
  // we wish to modify the 'href' attribute:
  attribute: 'href',
  // and we're selecing the <a> elements inside of <li> elements
  // inside of the <nav> element:
  elements: 'nav li a',
});
:root {
  --color: #000f;
  --backgroundColor: #fff;
}

*,
::before,
::after {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
  font-family: sans-serif;
  line-height: 1.5;
}

nav ul {
  display: flex;
  justify-content: space-around;
  list-style-type: none;
}

nav a:is(:link, :visited) {
  color: var(--color);
  background-color: var(--backgroundColor);
}

nav a:is(:hover, :active, :focus) {
  color: var(--backgroundColor);
  background-color: var(--color);
}

h2 {
  margin-block: 3em;
}

h2 a:is(:link, :visited) {
  background: linear-gradient(90deg, lime, #ffff);
  display: block;
  color: var(--color);
  text-decoration: none;
}

h2 a:is(:hover, :active, :focus) {
  text-decoration: underline;
  text-decoration-thickness: 3px;
}

h2 a:target {
  background: linear-gradient(90deg, #f90, #ffff);
}

h2 a::after {
  content: ' (#' attr(id) ').';
}
<nav id="toc">
  <ul>
    <li><a href="#test1">Link to "test1"</a></li>
    <li><a href="#best2">Link to "best2"</a></li>
    <li><a href="#nest3">Link to "nest3"</a></li>
    <li><a href="#rest4">Link to "rest4"</a></li>
  </ul>
</nav>
<h2>
  <a id="user-content-test1" href="https://www.example.com">
    Anything
  </a>
</h2>
<h2>
  <a id="user-content-best2" href="https://www.example.com">
    Anything
  </a>
</h2>
<h2>
  <a id="user-content-nest3" href="https://www.example.com">
    Anything
  </a>
</h2>
<h2>
  <a id="user-content-rest4" href="https://www.example.com">
    Anything
  </a>
</h2>

And with jQuery:

// here we're creating a jQuery plugin using the IIFE - Immediately-
// Invoked Function Expression - approach:
(function($) {

  // defining the name of the plugin, and passing in the opts argument:
  $.fn.insertTextToAttribute = function(opts) {
    // defining the defaults (as above), although again we're not
    // defining the element-types upon which we wish to work
    // as jQuery passes that collection to the plugin:
    let defaults = {
        attribute: 'id',
        endWith: false,
        insert: 'user-content-',
        startWith: false,
      },

      settings = $.extend(defaults, opts);

    // again using destructuring assignment:
    const {
      attribute,
      insert,
      startWith,
      endWith
    } = settings;

    // we're not filtering the collection here, since jQuery has
    // already taken care of finding the relevant <a> elements and
    // we don't need to select according to current attribute-values
    // since we're modifying them we may not know what they currently
    // are:
    return this.attr(attribute, function(i, attr) {

      // setting isHash to false:
      let isHash = false;

      // as above, we're using this - as jQuery makes that available
      // within its methods - and again using Element.matches, along
      // with checking that the current attribute-value - passed to
      // function automatically from jQuery - is equal to the current
      // hash of the element:
      if (this.matches('a') && attr === this.hash) {
        // if both those checks return true, we update the value to
        // true
        isHash = true;
      }
      
      // note that we could have written the above in the following way:
      // let isHash = this.matches('a') && attr === this.hash;
      // but using an if feels more readable (despite my subsequent
      // code)


      // this is almost the same as the above approach, using conditional
      // operators within template strings to interpolate various variables
      // into the string. The difference is that the 'currentValue'
      // variable was changed to the 'attr' variable-name (for no particular
      // reason):
      return `${isHash ? '#' : ''}${startWith ? insert : ''}${isHash ? attr.replace('#','') : attr}${endWith ? insert : ''}`

    });
  }

// passing jQuery into the function:
}(jQuery));

// selecting all <a> elements on the page that are within <li> elements
// and wrapped within a <nav> element:
$('nav li a').insertTextToAttribute({
  attribute: 'href',
  insert: 'user-content-',
  startWith: true
});
:root {
  --color: #000f;
  --backgroundColor: #fff;
}

*,
::before,
::after {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
  font-family: sans-serif;
  line-height: 1.5;
}

nav ul {
  display: flex;
  justify-content: space-around;
  list-style-type: none;
}

nav a:is(:link, :visited) {
  color: var(--color);
  background-color: var(--backgroundColor);
}

nav a:is(:hover, :active, :focus) {
  color: var(--backgroundColor);
  background-color: var(--color);
}

h2 {
  margin-block: 3em;
}

h2 a:is(:link, :visited) {
  background: linear-gradient(90deg, lime, #ffff);
  display: block;
  color: var(--color);
  text-decoration: none;
}

h2 a:is(:hover, :active, :focus) {
  text-decoration: underline;
  text-decoration-thickness: 3px;
}

h2 a:target {
  background: linear-gradient(90deg, #f90, #ffff);
}

h2 a::after {
  content: ' (#' attr(id) ').';
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<nav id="toc">
  <ul>
    <li><a href="#test1">Link to "test1"</a></li>
    <li><a href="#best2">Link to "best2"</a></li>
    <li><a href="#nest3">Link to "nest3"</a></li>
    <li><a href="#rest4">Link to "rest4"</a></li>
  </ul>
</nav>
<h2>
  <a id="user-content-test1" href="https://www.example.com">
    Anything
  </a>
</h2>
<h2>
  <a id="user-content-best2" href="https://www.example.com">
    Anything
  </a>
</h2>
<h2>
  <a id="user-content-nest3" href="https://www.example.com">
    Anything
  </a>
</h2>
<h2>
  <a id="user-content-rest4" href="https://www.example.com">
    Anything
  </a>
</h2>

Due to the character limits of Stack Overflow's answers I can't include a means to intercept the browser's click-events for <a> elements, though I imagine it's been written about elsewhere. Hopefully you're at least able to learn something useful from the answer regardless.

David Thomas
  • 249,100
  • 51
  • 377
  • 410