Search code examples
javascriptjquerytampermonkeyuserscripts

Get href of relatively located element


I'm trying to write a userscript that will

  1. add a checkbox after each hyperlink
  2. and then, after you click the checkbox, the corresponding hyperlink will change its state to "visited". (The color will be changed from blue to violet.)

The issue is that I don't understand how to "move" the href value from the a element to the desired_element variable.

To keep the example relatively simple, I use Wikipedia. However, in real life, it is intended for the different HTML structure, and thus it could be probably a good idea to use jQuery. (Probably .closest and then .find?)

Wikipedia case:

<p>In <a href="/wiki/Computer_programming">computer
programming<input type="checkbox"></a>, a naming convention
is...</p>
<!-- https://en.wikipedia.org/wiki/Naming_convention_(programming) -->

Real case:

<div>
    <figure>
        <div>
            <div>
                <img src="image.png">
            </div>
            <a href="https://example.com/>Click Me</a>
        </div>
    </div>
    <input type="checkbox">
</div>
// ==UserScript==
// @grant   none
// @match   https://*.wikipedia.org/*
// @name    Wikipedia
// @require https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js
// ==/UserScript==

(function() {
    'use strict';

    function actionFunction() {
        var links = document.querySelectorAll('a');
        var i;
        for (i = 0; i < links.length; i++) {
            var input = document.createElement('input');
            input.setAttribute('type', 'checkbox');
            //input.addEventListener("change", aaa);
            input.onchange = function() {aaa()};
            links[i].appendChild(input);          
        }
    }

    function aaa() {
        var current_url;
        // var desired_url = something?.parentNode.href;

        // store the current URL
        current_url = window.location.href;

        // use replaceState to push a new entry into the browser's history
        history.replaceState({}, '', desired_url);

        // use replaceState again to reset the URL
        history.replaceState({}, '', current_url);
    }

    actionFunction();
})();

Solution

  • To make this work you need to get a reference to the element in the aaa() function. To do that you could pass it as an argument, or you can use addEventListener and use this within the event handler to reference the element which raised the event. The latter would be better practice.

    However it's worth noting that you cannot have a checkbox within an a element because you cannot nest clickable elements. The input needs to be a sibling of the a, which can be achieved by appending to the parent. You can also store the URL as a data attribute in the input instead of having to traverse the DOM to find the related a. Try this:

    function actionFunction() {
      var links = document.querySelectorAll('a');
      var i;
      for (i = 0; i < links.length; i++) {
        var input = document.createElement('input');
        input.type = 'checkbox';
        input.dataset.url = links[i].href;
        input.addEventListener("change", aaa);
        links[i].parentElement.insertBefore(input, links[i].nextSibling);
      }
    }
    
    function aaa() {
      let desired_url = this.dataset.url;
      let current_url = window.location.href;
      history.replaceState({}, '', desired_url);
      history.replaceState({}, '', current_url);
    }
    
    actionFunction();