Get link on webpage with specific text

I've read various posts about Xpath and working with web elements. Could someone help me learn by sharing a macro that finds the link on Google.com with the text "About" and displays its URL?

For example, I can open the link using Click on the first web page item that matches an XPath with this xpath //a[contains(., "about")] - but I am just trying to learn the simple steps of 1) Finding a link by its text, and 2) returning the URL as a variable or display results.

Sharing this progress in case it helps anyone. Firing this macro from Safari at google.com will find the link with the link text "About" and display its URL.

(function (strPath) {
    const link = document.evaluate(strPath, document, null, XPathResult.ANY_TYPE, null).iterateNext();
    if (link) {
        return link.href;
    } else {
        return null;
    }
})(document.kmvar.local_xPath);

And here's one way of collecting ALL links on the active page for which the label text contains a given string.

(Assumes Keyboard Maestro 11+)

ALL links containing text.kmmacros (4.8 KB)


Expand disclosure triangle to view JS source
const main = () =>
    Array.from(
        xPathMatches( kmvar.local_xPath )
    )
    .map(x => `[${x.textContent}](${x.href})`)
    .join("\n");


// --------------------- GENERIC ---------------------

// Just :: a -> Maybe a
const Just = x => ({
    type: "Maybe",
    Just: x
});

// Nothing :: Maybe a
const Nothing = () => ({
    type: "Maybe",
    Nothing: true
});

// xPathMatches :: XPath String -> Gen [Node]
const xPathMatches = xpath =>
    // A list of any matching nodes in the document.
    // (Can be made strict by wrapping in Array.from)
    unfoldr(x => {
        const v = x.iterateNext();

        return Boolean(v)
            ? Just([v, x])
            : Nothing();
    })(
        document.evaluate(
            xpath,
            document,
            null,
            XPathResult.ORDERED_NODE_ITERATOR_TYPE
        )
    );

// unfoldr :: (b -> Maybe (a, b)) -> b -> Gen [a]
const unfoldr = f =>
    // A lazy (generator) list unfolded from a seed value
    // by repeated application of f to a value until no
    // residue remains. Dual to fold/reduce.
    // f returns either Nothing or Just (value, residue).
    // For a strict output list,
    // wrap with `list` or Array.from
    x => (
        function* () {
            let maybePair = f(x);

            while (!maybePair.Nothing) {
                const valueResidue = maybePair.Just;

                yield valueResidue[0];
                maybePair = f(valueResidue[1]);
            }
        }()
    );

// MAIN ---
return main();
1 Like

I've only started with Javascript, so this is over my head in terms of learning for now, but I can certainly use it. I see you're adding all matches to an array and creating a list of links from the array variable. I'll come back to the code when I know a little more.

Thanks!

1 Like

Note that the Modern Syntax option in Keyboard Maestro 11 (see options in small chevron menu to left of the const in the JS text field below)

  1. Allows a simpler reference to Keyboard Maestro variable values,
  2. uses a return expression to make a value available to the calling macro.

For example, to return only the first Xpath match:

FIRST link containing text.kmmacros (3.7 KB)


Expand disclosure triangle to view JS source
const 
    firstMatch =  document.evaluate(
        kmvar.local_xPath,
        document,
        null,
        XPathResult.ORDERED_NODE_ITERATOR_TYPE
    )
    .iterateNext()

return `[${firstMatch.textContent}](${firstMatch.href})`;