{"id":1107,"date":"2025-11-13T07:00:00","date_gmt":"2025-11-13T08:00:00","guid":{"rendered":"https:\/\/computercoursesonline.com\/?p=1107"},"modified":"2025-11-13T20:58:43","modified_gmt":"2025-11-13T20:58:43","slug":"older-tech-in-the-browser-stack","status":"publish","type":"post","link":"https:\/\/computercoursesonline.com\/index.php\/2025\/11\/13\/older-tech-in-the-browser-stack\/","title":{"rendered":"Older Tech In The Browser Stack"},"content":{"rendered":"

Older Tech In The Browser Stack<\/title><\/p>\n<article>\n<header>\n<h1>Older Tech In The Browser Stack<\/h1>\n<address>Bryan Rasmussen<\/address>\n<p> 2025-11-13T08:00:00+00:00<br \/>\n 2025-11-13T20:32:27+00:00<br \/>\n <\/header>\n<p>I\u2019ve been in front-end development long enough to see a trend over the years: younger developers working with a new paradigm of programming without understanding the historical context of it.<\/p>\n<p>It is, of course, perfectly understandable to <em>not<\/em> know something. The web is a very big place with a diverse set of skills and specialties, and we don\u2019t always know what we don\u2019t know. Learning in this field is an ongoing journey rather than something that happens once and ends.<\/p>\n<p>Case in point: Someone on my team asked if it was possible to tell if users navigate away from a particular tab in the UI. I pointed out JavaScript\u2019s <a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/API\/Window\/beforeunload_event\"><code>beforeunload<\/code> event<\/a>. But those who have tackled this before know this is possible because they have been hit with alerts about unsaved data on other sites, for which <code>beforeunload<\/code> is a typical use case. I also pointed out the <a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/API\/Window\/pagehide_event\"><code>pageHide<\/code><\/a> and <a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/API\/Document\/visibilitychange_event\"><code>visibilityChange<\/code><\/a> events to my colleague for good measure.<\/p>\n<p>How did I know about that? Because it came up in another project, not because I studied up on it when initially learning JavaScript.<\/p>\n<p>The fact is that modern front-end frameworks are standing on the shoulders of the technology giants that preceded them. They abstract development practices, often for a better developer experience that reduces, or even eliminates, the need to know or touch what have traditionally been essential front-end concepts everyone probably ought to know.<\/p>\n<p>Consider the <a href=\"https:\/\/css-tricks.com\/an-introduction-and-guide-to-the-css-object-model-cssom\/\">CSS Object Model (CSSOM)<\/a>. You might expect that anyone working in CSS and JavaScript has a bunch of hands-on CSSOM experience, but that\u2019s not always going to be the case.<\/p>\n<p>There was a React project for an e-commerce site I worked on where we needed to load a stylesheet for the currently selected payment provider. The problem was that the stylesheet was loading on every page when it was only really needed on a specific page. The developer tasked with making this happen hadn\u2019t ever loaded a stylesheet dynamically. Again, this is totally understandable when React abstracts away the traditional approach you might have reached for.<\/p>\n<p>The CSSOM is likely not something you need in your everyday work. But it is likely you will need to interact with it at some point, even in a one-off instance.<\/p>\n<p>These experiences inspired me to write this article. There are many existing web features and technologies in the wild that you may never touch directly in your day-to-day work. Perhaps you\u2019re fairly new to web development and are simply unaware of them because you\u2019re steeped in the abstraction of a specific framework that doesn\u2019t require you to know it deeply, or even at all.<\/p>\n<p>I\u2019m speaking specifically about <a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/XML\/Guides\/XML_introduction\">XML<\/a>, which many of us know is an ancient language not totally dissimilar from HTML.<\/p>\n<p>I\u2019m bringing this up because of recent WHATWG discussions <a href=\"https:\/\/github.com\/whatwg\/html\/issues\/11523\">suggesting<\/a> that a significant chunk of the XML stack known as <a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/XML\/XSLT\">XSLT<\/a> programming should be removed from browsers. This is exactly the sort of older, existing technology we\u2019ve had for years that could be used for something as practical as the CSSOM situation my team was in.<\/p>\n<p>Have you worked with XSLT before? Let\u2019s see if we lean heavily into this older technology and leverage its features outside the context of XML to tackle real-world problems today.<\/p>\n<h2 id=\"xpath-the-central-api\">XPath: The Central API<\/h2>\n<p>The most important XML technology that is perhaps the most useful outside of a straight XML perspective is <strong>XPath<\/strong>, a query language that allows you to find any node or attribute in a markup tree with one root element. I have a personal affection for XSLT, but that also relies on XPath, and personal affection must be put aside in ranking importance.<\/p>\n<p>The argument for removing XSLT does not make any mention of XPath, so I suppose it is still allowed. That\u2019s good because XPath is the central and most important API in this suite of technologies, especially when trying to find something to use outside normal XML usage. It is important because, while CSS selectors can be used to find most of the elements in your page, they cannot find them all. Furthermore, CSS selectors cannot be used to find an element based on its current position in the DOM.<\/p>\n<p>XPath can.<\/p>\n<p>Now, some of you reading this might know XPath, and some might not. XPath is a pretty big area of technology, and I can\u2019t really teach all the basics and also show you cool things to do with it in a single article like this. I actually tried writing that article, but the average Smashing Magazine publication doesn\u2019t go over 5,000 words. I was already at more than 2,000 words while only halfway through the basics.<\/p>\n<p>So, I\u2019m going to start doing cool stuff with XPath and give you some links that you can use for the basics if you find this stuff interesting.<\/p>\n<h2 id=\"combining-xpath-css\">Combining XPath & CSS<\/h2>\n<p>XPath can do lots of things that CSS selectors can\u2019t when querying elements. But CSS selectors can also do a few things that XPath can\u2019t, namely, query elements by class name.<\/p>\n<table class=\"tablesaw break-out\">\n<thead>\n<tr>\n<th>CSS<\/th>\n<th>XPath<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td><code>.myClass<\/code><\/td>\n<td><code>\/*[contains(@class, \"myClass\")]<\/code><\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>In this example, CSS queries elements that contain a <code>.myClass<\/code> classname. Meanwhile, the XPath example queries elements that contain an attribute class with the string \u201c<code>myClass<\/code>\u201d. In other words, it selects elements with <code>myClass<\/code> in any attribute, including elements with the <code>.myClass<\/code> classname — as well as elements with \u201c<code>myClass<\/code>\u201d in the string, such as <code>.myClass2<\/code>. XPath is broader in that sense.<\/p>\n<p>So, no. I\u2019m not suggesting that we ought to toss out CSS and start selecting all elements via XPath. That\u2019s not the point.<\/p>\n<blockquote class=\"pull-quote\">\n<p>\n <a class=\"pull-quote__link\" aria-label=\"Share on Twitter\" href=\"https:\/\/twitter.com\/share?text=%0aThe%20point%20is%20that%20XPath%20can%20do%20things%20that%20CSS%20cannot%20and%20could%20still%20be%20very%20useful,%20even%20though%20it%20is%20an%20older%20technology%20in%20the%20browser%20stack%20and%20may%20not%20seem%20obvious%20at%20first%20glance.%0a&url=https:\/\/smashingmagazine.com%2f2025%2f11%2folder-tech-browser-stack%2f\"><\/p>\n<p>The point is that XPath can do things that CSS cannot and could still be very useful, even though it is an older technology in the browser stack and may not seem obvious at first glance.<\/p>\n<p> <\/a>\n <\/p>\n<div class=\"pull-quote__quotation\">\n<div class=\"pull-quote__bg\">\n <span class=\"pull-quote__symbol\">\u201c<\/span><\/div>\n<\/p><\/div>\n<\/blockquote>\n<p>Let\u2019s use the two technologies together not only because we can, but because we\u2019ll learn something about XPath in the process, making it another tool in your stack — one you might not have known has been there all along!<\/p>\n<p>The problem is that JavaScript\u2019s <code>document.evaluate<\/code> method and the various query selector methods we use with the CSS APIs for JavaScript are incompatible.<\/p>\n<p>I have made a compatible querying API to get us started, though admittedly, I have not put a lot of thought into it since it\u2019s a departure from what we\u2019re doing here. Here\u2019s a fairly simple working example of a reusable query constructor:<\/p>\n<figure class=\"break-out\">\n<p data-height=\"480\" data-theme-id=\"light\" data-slug-hash=\"jEqEyEx\" data-user=\"smashingmag\" data-default-tab=\"result\" class=\"codepen\">See the Pen [queryXPath [forked]](https:\/\/codepen.io\/smashingmag\/pen\/jEqEyEx) by <a href=\"https:\/\/codepen.io\/bryanrasmussen\">Bryan Rasmussen<\/a>.<\/p><figcaption>See the Pen <a href=\"https:\/\/codepen.io\/smashingmag\/pen\/jEqEyEx\">queryXPath [forked]<\/a> by <a href=\"https:\/\/codepen.io\/bryanrasmussen\">Bryan Rasmussen<\/a>.<\/figcaption><\/figure>\n<p>I\u2019ve added two methods on the document object: <code>queryCSSSelectors<\/code> (which is essentially <code>querySelectorAll<\/code>) and <code>queryXPaths<\/code>. Both of these return a <code>queryResults<\/code> object:<\/p>\n<div class=\"break-out\">\n<pre><code class=\"language-javascript\">{\n queryType: nodes | string | number | boolean,\n results: any[] \/\/ html elements, xml elements, strings, numbers, booleans,\n queryCSSSelectors: (query: string, amend: boolean) => queryResults,\n queryXpaths: (query: string, amend: boolean) => queryResults\n}\n<\/code><\/pre>\n<\/div>\n<p>The <code>queryCSSSelectors<\/code> and <code>queryXpaths<\/code> functions run the query you give them over the elements in the results array, as long as the results array is of type <code>nodes<\/code>, of course. Otherwise, it will return a <code>queryResult<\/code> with an empty array and a type of <code>nodes<\/code>. If the <code>amend<\/code> property is set to <code>true<\/code>, the functions will change their own <code>queryResults<\/code>.<\/p>\n<p><strong>Under no circumstances should this be used in a production environment.<\/strong> I am doing it this way purely to demonstrate the various effects of using the two query APIs together.<\/p>\n<h2 id=\"example-queries\">Example Queries<\/h2>\n<p>I want to show a few examples of different XPath queries that demonstrate some of the powerful things they can do and how they can be used in place of other approaches.<\/p>\n<p>The first example is <code>\/\/li\/text()<\/code>. This queries all <code>li<\/code> elements and returns their text nodes. So, if we were to query the following HTML:<\/p>\n<pre><code class=\"language-html\"><ul>\n <li>one<\/li>\n <li>two<\/li>\n <li>three<\/li>\n<\/ul>\n<\/code><\/pre>\n<p>\u2026this is what is returned:<\/p>\n<div class=\"break-out\">\n<pre><code class=\"language-json\">{\"queryType\":\"xpathEvaluate\",\"results\":[\"one\",\"two\",\"three\"],\"resultType\":\"string\"}\n<\/code><\/pre>\n<\/div>\n<p>In other words, we get the following array: <code>["one","two","three"]<\/code>.<\/p>\n<p>Normally, you would query for the <code>li<\/code> elements to get that, turn the result of that query into an array, map the array, and return the text node of each element. But we can do that more concisely with XPath:<\/p>\n<pre><code class=\"language-javascript\">document.queryXPaths(\"\/\/li\/text()\").results.\n<\/code><\/pre>\n<p>Notice that the way to get a text node is to use <code>text()<\/code>, which looks like a function signature — and it is. It returns the text node of an element. In our example, there are three <code>li<\/code> elements in the markup, each containing text (<code>"one"<\/code>, <code>"two"<\/code>, and <code>"three"<\/code>).<\/p>\n<p>Let\u2019s look at one more example of a <code>text()<\/code> query. Assume this is our markup:<\/p>\n<pre><code class=\"language-html\"><pa href=\"\/login.html\">Sign In<\/a>\n<\/code><\/pre>\n<p>Let\u2019s write a query that returns the <code>href<\/code> attribute value:<\/p>\n<pre><code class=\"language-javascript\">document.queryXPaths(\"\/\/a[text() = 'Sign In']\/@href\").results.\n<\/code><\/pre>\n<p>This is an XPath query on the current document, just like the last example, but this time we return the <code>href<\/code> attribute of a link (<code>a<\/code> element) that contains the text \u201cSign In\u201d. The actual returned result is <code>["\/login.html"]<\/code>.<\/p>\n<h2 id=\"xpath-functions-overview\">XPath Functions Overview<\/h2>\n<p>There are a number of XPath functions, and you\u2019re probably unfamiliar with them. There are several, I think, that are worth knowing about, including the following:<\/p>\n<ul>\n<li><strong><code>starts-with<\/code><\/strong><br \/>\nIf a text starts with a particular other text example, <code>starts-with(@href, 'http:')<\/code> returns <code>true<\/code> if an <code>href<\/code> attribute starts with <code>http:<\/code>.<\/li>\n<li><strong><code>contains<\/code><\/strong><br \/>\nIf a text contains a particular other text example, <code>contains(text(), "Smashing Magazine")<\/code> returns <code>true<\/code> if a text node contains the words \u201cSmashing Magazine\u201d in it anywhere.<\/li>\n<li><strong><code>count<\/code><\/strong><br \/>\nReturns a count of how many matches there are to a query. For example, <code>count(\/\/*[starts-with(@href, 'http:'])<\/code> returns a count of how many links in the context node have elements with an <code>href<\/code> attribute that contains the text beginning with the <code>http:<\/code>.<\/li>\n<li><strong><code>substring<\/code><\/strong><br \/>\nWorks like JavaScript <code>substring<\/code>, except you pass the string as an argument. For example, <code>substring("my text", 2, 4)<\/code> returns <code>"y t"<\/code>.<\/li>\n<li><strong><code>substring-before<\/code><\/strong><br \/>\nReturns the part of a string before another string. For example, <code>substing-before("my text", " ")<\/code> returns <code>"my"<\/code>. Similarly, <code>substring-before("hi","bye")<\/code> returns an empty string.<\/li>\n<li><strong><code>substring-after<\/code><\/strong><br \/>\nReturns the part of a string after another string. For example, <code>substing-after("my text", " ")<\/code> returns <code>"text"<\/code>. Similarly, <code>substring-after("hi","bye")<\/code>returns an empty string.<\/li>\n<li><strong><code>normalize-space<\/code><\/strong><br \/>\nReturns the argument string with whitespace normalized by stripping leading and trailing whitespace and replacing sequences of whitespace characters by a single space.<\/li>\n<li><strong><code>not<\/code><\/strong><br \/>\nReturns a boolean <code>true<\/code> if the argument is false, otherwise <code>false<\/code>.<\/li>\n<li><strong><code>true<\/code><\/strong><br \/>\nReturns boolean <code>true<\/code>.<\/li>\n<li><strong><code>false<\/code><\/strong><br \/>\nReturns boolean <code>false<\/code>.<\/li>\n<li><strong><code>concat<\/code><\/strong><br \/>\nThe same thing as JavaScript <code>concat<\/code>, except you do not run it as a method on a string. Instead, you put in all the strings you want to concatenate.<\/li>\n<li><strong><code>string-length<\/code><\/strong><br \/>\nThis is not the same as JavaScript <code>string-length<\/code>, but rather returns the length of the string it is given as an argument.<\/li>\n<li><strong><code>translate<\/code><\/strong><br \/>\nThis takes a string and changes the second argument to the third argument. For example, <code>translate("abcdef", "abc", "XYZ")<\/code> outputs <code>XYZdef<\/code>.<\/li>\n<\/ul>\n<p>Aside from these particular XPath functions, there are a number of other functions that work just the same as their JavaScript counterparts — or counterparts in basically any programming language — that you would probably also find useful, such as <code>floor<\/code>, <code>ceiling<\/code>, <code>round<\/code>, <code>sum<\/code>, and so on.<\/p>\n<p>The following demo illustrates each of these functions:<\/p>\n<figure class=\"break-out\">\n<p data-height=\"480\" data-theme-id=\"light\" data-slug-hash=\"emZmgzX\" data-user=\"smashingmag\" data-default-tab=\"result\" class=\"codepen\">See the Pen [XPath Numerical functions [forked]](https:\/\/codepen.io\/smashingmag\/pen\/emZmgzX) by <a href=\"https:\/\/codepen.io\/bryanrasmussen\">Bryan Rasmussen<\/a>.<\/p><figcaption>See the Pen <a href=\"https:\/\/codepen.io\/smashingmag\/pen\/emZmgzX\">XPath Numerical functions [forked]<\/a> by <a href=\"https:\/\/codepen.io\/bryanrasmussen\">Bryan Rasmussen<\/a>.<\/figcaption><\/figure>\n<p>Note that, like most of the string manipulation functions, many of the numerical ones take a <strong>single input<\/strong>. This is, of course, because they are supposed to be used for querying, as in the last XPath example:<\/p>\n<pre><code class=\"language-html\">\/\/li[floor(text()) > 250]\/@val\n<\/code><\/pre>\n<p>If you use them, as most of the examples do, you will end up running it on the first node that matches the path.<\/p>\n<p>There are also some type conversion functions you should probably avoid because JavaScript already has its own type conversion problems. But there can be times when you want to convert a string to a number in order to check it against some other number.<\/p>\n<p>Functions that set the type of something are boolean, number, string, and node. These are the important XPath datatypes.<\/p>\n<p>And as you might imagine, most of these functions can be used on datatypes that are not DOM nodes. For example, <code>substring-after<\/code> takes a string as we\u2019ve already covered, but it could be the string from an <code>href<\/code> attribute. It can also just be a string:<\/p>\n<div class=\"break-out\">\n<pre><code class=\"language-javascript\">const testSubstringAfter = document.queryXPaths(\"substring-after('hello world',' ')\");\n<\/code><\/pre>\n<\/div>\n<p>Obviously, this example will give us back the results array as <code>["world"]<\/code>. To show this in action, I have made a demo page using functions against things that are not DOM nodes:<\/p>\n<figure class=\"break-out\">\n<p data-height=\"480\" data-theme-id=\"light\" data-slug-hash=\"qEZERqd\" data-user=\"smashingmag\" data-default-tab=\"result\" class=\"codepen\">See the Pen [queryXPath [forked]](https:\/\/codepen.io\/smashingmag\/pen\/qEZERqd) by <a href=\"https:\/\/codepen.io\/bryanrasmussen\">Bryan Rasmussen<\/a>.<\/p><figcaption>See the Pen <a href=\"https:\/\/codepen.io\/smashingmag\/pen\/qEZERqd\">queryXPath [forked]<\/a> by <a href=\"https:\/\/codepen.io\/bryanrasmussen\">Bryan Rasmussen<\/a>.<\/figcaption><\/figure>\n<p>You should note the surprising aspect of the <code>translate<\/code> function, which is that if you have a character in the second argument (i.e., the list of characters you want translated) and no matching character to translate to, that character gets removed from the output.<\/p>\n<p>Thus, this:<\/p>\n<div class=\"break-out\">\n<pre><code class=\"language-javascript\">translate('Hello, My Name is Inigo Montoya, you killed my father, prepare to die','abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ,','*')\n<\/code><\/pre>\n<\/div>\n<p>\u2026results in the string, including spaces:<\/p>\n<pre><code class=\"language-json\">[\" * * ** \"]\n<\/code><\/pre>\n<p>This means that the letter \u201ca\u201d is being translated to an asterisk (<code>*<\/code>), but every other character that does not have a translation given the target string is completely removed. The whitespace is all we have left between the translated \u201ca\u201d characters.<\/p>\n<p>Then again, this query:<\/p>\n<div class=\"break-out\">\n<pre><code class=\"language-javascript\">translate('Hello, My Name is Inigo Montoya, you killed my father, prepare to die','abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ,','**************************************************')\")\n<\/code><\/pre>\n<\/div>\n<p>\u2026does not have the problem and outputs a result that looks like this:<\/p>\n<div class=\"break-out\">\n<pre><code class=\"language-javascript\">\"***** ** **** ** ***** ******* *** ****** ** ****** ******* ** ***\"\n<\/code><\/pre>\n<\/div>\n<p>It might strike you that there is no easy way in JavaScript to do exactly what the XPath <code>translate<\/code> function does, although for many use cases, <code>replaceAll<\/code> with regular expressions can handle it.<\/p>\n<p>You could use the same approach I have demonstrated, but that is suboptimal if all you want is to translate the strings. The following demo wraps XPath\u2019s <code>translate<\/code> function to provide a JavaScript version:<\/p>\n<figure class=\"break-out\">\n<p data-height=\"480\" data-theme-id=\"light\" data-slug-hash=\"ZYWYLyZ\" data-user=\"smashingmag\" data-default-tab=\"result\" class=\"codepen\">See the Pen [translate function [forked]](https:\/\/codepen.io\/smashingmag\/pen\/ZYWYLyZ) by <a href=\"https:\/\/codepen.io\/bryanrasmussen\">Bryan Rasmussen<\/a>.<\/p><figcaption>See the Pen <a href=\"https:\/\/codepen.io\/smashingmag\/pen\/ZYWYLyZ\">translate function [forked]<\/a> by <a href=\"https:\/\/codepen.io\/bryanrasmussen\">Bryan Rasmussen<\/a>.<\/figcaption><\/figure>\n<p>Where might you use something like this? Consider <a href=\"https:\/\/en.wikipedia.org\/wiki\/Caesar_cipher\">Caesar Cipher<\/a> encryption with a three-place offset (e.g., top-of-the-line encryption from 48 B.C.):<\/p>\n<div class=\"break-out\">\n<pre><code class=\"language-javascript\">translate(\"Caesar is planning to cross the Rubicon!\", \n \"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz\",\n \"XYZABCDEFGHIJKLMNOPQRSTUVWxyzabcdefghijklmnopqrstuvw\")\n<\/code><\/pre>\n<\/div>\n<p>The input text \u201cCaesar is planning to cross the Rubicon!\u201d results in \u201cZxbpxo fp mixkkfkd ql zolpp qeb Oryfzlk!\u201d<\/p>\n<p>To give another quick example of different possibilities, I made a <code>metal<\/code> function that takes a string input and uses a <code>translate<\/code> function to return the text, including all characters that take umlauts.<\/p>\n<figure class=\"break-out\">\n<p data-height=\"480\" data-theme-id=\"light\" data-slug-hash=\"YPqPNrN\" data-user=\"smashingmag\" data-default-tab=\"result\" class=\"codepen\">See the Pen [metal function [forked]](https:\/\/codepen.io\/smashingmag\/pen\/YPqPNrN) by <a href=\"https:\/\/codepen.io\/bryanrasmussen\">Bryan Rasmussen<\/a>.<\/p><figcaption>See the Pen <a href=\"https:\/\/codepen.io\/smashingmag\/pen\/YPqPNrN\">metal function [forked]<\/a> by <a href=\"https:\/\/codepen.io\/bryanrasmussen\">Bryan Rasmussen<\/a>.<\/figcaption><\/figure>\n<div class=\"break-out\">\n<pre><code class=\"language-javascript\">const metal = (str) => {\n return translate(str, \"AOUaou\",\"\u00c4\u00d6\u00dc\u00e4\u00f6\u00fc\");\n}\n<\/code><\/pre>\n<\/div>\n<p>And, if given the text \u201cMotley Crue rules, rock on dudes!\u201d, returns \u201cM\u00f6tley Cr\u00fce r\u00fcles, r\u00f6ck \u00f6n d\u00fcdes!\u201d<\/p>\n<p>Obviously, one might have all sorts of parody uses of this function. If that\u2019s you, then this <a href=\"https:\/\/tvtropes.org\/pmwiki\/pmwiki.php\/Main\/HeavyMetalUmlaut\">TVTropes article<\/a> ought to provide you with plenty of inspiration.<\/p>\n<h2 id=\"using-css-with-xpath\">Using CSS With XPath<\/h2>\n<p>Remember our main reason for using CSS selectors together with XPath: CSS pretty much understands what a class is, whereas the best you can do with XPath is string comparisons of the class attribute. That will work in most cases.<\/p>\n<p>But if you were to ever run into a situation where, say, someone created classes named <code>.primaryLinks<\/code> and <code>.primaryLinks2<\/code> and you were using XPath to get the <code>.primaryLinks<\/code> class, then you would likely run into problems. As long as there\u2019s nothing silly like that, you would probably use XPath. But I am sad to report that I have worked at places where people do those types of silly things.<\/p>\n<p>Here\u2019s another demo using CSS and XPath together. It shows what happens when we use the code to run an XPath on a context node that is not the document\u2019s node.<\/p>\n<figure class=\"break-out\">\n<p data-height=\"480\" data-theme-id=\"light\" data-slug-hash=\"ogxgBpz\" data-user=\"smashingmag\" data-default-tab=\"result\" class=\"codepen\">See the Pen [css and xpath together [forked]](https:\/\/codepen.io\/smashingmag\/pen\/ogxgBpz) by <a href=\"https:\/\/codepen.io\/bryanrasmussen\">Bryan Rasmussen<\/a>.<\/p><figcaption>See the Pen <a href=\"https:\/\/codepen.io\/smashingmag\/pen\/ogxgBpz\">css and xpath together [forked]<\/a> by <a href=\"https:\/\/codepen.io\/bryanrasmussen\">Bryan Rasmussen<\/a>.<\/figcaption><\/figure>\n<p>The CSS query is <code>.relatedarticles a<\/code>, which fetches the two <code>a<\/code> elements in a <code>div<\/code> assigned a <code>.relatedarticles<\/code> class.<\/p>\n<p>After that are three \u201cbad\u201d queries, that is to say, queries that do not do what we want them to do when running with these elements as the context node.<\/p>\n<p>I can explain why they are behaving differently than you might expect. The three bad queries in question are:<\/p>\n<ul>\n<li><code>\/\/text()<\/code>: Returns all the text in the document.<\/li>\n<li><code>\/\/a\/text()<\/code>: Returns all the text inside of links in the document.<\/li>\n<li><code>.\/a\/text()<\/code>: Returns no results.<\/li>\n<\/ul>\n<p>The reason for these results is that while your context is <code>a<\/code> elements returned from the CSS query, <code>\/\/<\/code> goes against the whole document. This is the strength of XPath; CSS cannot go from a node up to an ancestor and then to a sibling of that ancestor, and walk down to a descendant of that sibling. But XPath can.<\/p>\n<p>Meanwhile, <code>.\/<\/code> queries the children of the current node, where the dot (<code>.<\/code>) represents the current node, and the forward slash (<code>\/<\/code>) represents going to some child node — whether it is an attribute, element, or text is determined by the next part of the path. But there is no child <code>a<\/code> element selected by the CSS query, thus that query also returns nothing.<\/p>\n<p>There are three good queries in that last demo:<\/p>\n<ul>\n<li><code>.\/\/text()<\/code>,<\/li>\n<li><code>.\/text()<\/code>,<\/li>\n<li><code>normalize-space(.\/text())<\/code>.<\/li>\n<\/ul>\n<p>The <code>normalize-space<\/code> query demonstrates XPath function usage, but also fixes a problem included in the other queries. The HTML is structured like this:<\/p>\n<div class=\"break-out\">\n<pre><code class=\"language-html\"><a href=\"https:\/\/www.smashingmagazine.com\/2018\/04\/feature-testing-selenium-webdriver\/\">\n Automating Your Feature Testing With Selenium WebDriver\n<\/a>\n<\/code><\/pre>\n<\/div>\n<p>The query returns a line feed at the beginning and end of the text node, and <code>normalize-space<\/code> removes this.<\/p>\n<p>Using any XPath function that returns something other than a boolean with an input XPath applies to other functions. The following demo shows a number of examples:<\/p>\n<figure class=\"break-out\">\n<p data-height=\"480\" data-theme-id=\"light\" data-slug-hash=\"JoXYGeN\" data-user=\"smashingmag\" data-default-tab=\"result\" class=\"codepen\">See the Pen [xpath functions examples [forked]](https:\/\/codepen.io\/smashingmag\/pen\/JoXYGeN) by <a href=\"https:\/\/codepen.io\/bryanrasmussen\">Bryan Rasmussen<\/a>.<\/p><figcaption>See the Pen <a href=\"https:\/\/codepen.io\/smashingmag\/pen\/JoXYGeN\">xpath functions examples [forked]<\/a> by <a href=\"https:\/\/codepen.io\/bryanrasmussen\">Bryan Rasmussen<\/a>.<\/figcaption><\/figure>\n<p>The first example shows a problem you should watch out for. Specifically, the following code:<\/p>\n<div class=\"break-out\">\n<pre><code class=\"language-javascript\">document.queryXPaths(\"substring-after(\/\/a\/@href,'https:\/\/')\");\n<\/code><\/pre>\n<\/div>\n<p>\u2026returns one string:<\/p>\n<div class=\"break-out\">\n<pre><code class=\"language-html\">\"www.smashingmagazine.com\/2018\/04\/feature-testing-selenium-webdriver\/\"\n<\/code><\/pre>\n<\/div>\n<p>It makes sense, right? These functions do not return arrays but rather single strings or single numbers. Running the function anywhere with multiple results only returns the first result.<\/p>\n<p>The second result shows what we really want:<\/p>\n<div class=\"break-out\">\n<pre><code class=\"language-javascript\">document.queryCSSSelectors(\"a\").queryXPaths(\"substring-after(.\/@href,'https:\/\/')\");\n<\/code><\/pre>\n<\/div>\n<p>Which returns an array of two strings:<\/p>\n<div class=\"break-out\">\n<pre><code class=\"language-json\">[\"www.smashingmagazine.com\/2018\/04\/feature-testing-selenium-webdriver\/\",\"www.smashingmagazine.com\/2022\/11\/automated-test-results-improve-accessibility\/\"]\n<\/code><\/pre>\n<\/div>\n<p>XPath functions can be nested just like functions in JavaScript. So, if we know the Smashing Magazine URL structure, we could do the following (using template literals is recommended):<\/p>\n<pre><code class=\"language-javascript\">`translate(\n substring(\n substring-after(.\/@href, \u2018www.smashingmagazine.com\/')\n ,9),\n '\/','')`\n<\/code><\/pre>\n<p>This is getting a bit too complex to the extent that it needs comments describing what it does: take all of the URL from the <code>href<\/code> attribute after <code>www.smashingmagazine.com\/<\/code>, remove the first nine characters, then translate the forward slash (<code>\/<\/code>) character to nothing so as to get rid of the ending forward slash.<\/p>\n<p>The resulting array:<\/p>\n<div class=\"break-out\">\n<pre><code class=\"language-json\">[\"feature-testing-selenium-webdriver\",\"automated-test-results-improve-accessibility\"]\n<\/code><\/pre>\n<\/div>\n<h2 id=\"more-xpath-use-cases\">More XPath Use Cases<\/h2>\n<p>XPath can really shine in <strong>testing<\/strong>. The reason is not difficult to see, as XPath can be used to get every element in the DOM, from any position in the DOM, whereas CSS cannot.<\/p>\n<p>You cannot count on CSS classes remaining consistent in many modern build systems, but with XPath, we are able to make more robust matches as to what the text content of an element is, regardless of a changing DOM structure.<\/p>\n<p>There has been <a href=\"https:\/\/ieeexplore.ieee.org\/document\/6983884\">research on techniques<\/a> that allow you to make resilient XPath tests. Nothing is worse than having tests flake out and fail just because a CSS selector no longer works because something has been renamed or removed.<\/p>\n<p>XPath is also really great at <strong>multiple locator extraction<\/strong>. There is more than one way to use XPath queries to match an element. The same is true with CSS. But XPath queries can drill into things in a more targeted way that limits what gets returned, allowing you to find a specific match where there may be several possible matches.<\/p>\n<p>For example, we can use XPath to return a specific <code>h2<\/code> element that is contained inside a <code>div<\/code> that immediately follows a sibling <code>div<\/code> that, in turn, contains a child image element with a <code>data-testID="leader"<\/code> attribute on it:<\/p>\n<pre><code class=\"language-html\"><div>\n <div>\n <h1>don't get this headline<\/h1>\n <\/div>\n \n <div>\n <h2>Don't get this headline either<\/h2>\n <\/div>\n \n <div>\n <h2>The header for the leader image<\/h2>\n <\/div>\n \n <div>\n <img data-testID=\"leader\" src=\"image.jpg\"\/>\n <\/div>\n<\/div>\n<\/code><\/pre>\n<p>This is the query:<\/p>\n<pre><code class=\"language-javascript\">document.queryXPaths(`\n \/\/div[\n following-sibling::div[1]\n \/img[@data-testID='leader']\n ]\n \/h2\/\n text()\n`);\n<\/code><\/pre>\n<p>Let\u2019s drop in a demo to see how that all comes together:<\/p>\n<figure class=\"break-out\">\n<p data-height=\"480\" data-theme-id=\"light\" data-slug-hash=\"zxqxNev\" data-user=\"smashingmag\" data-default-tab=\"result\" class=\"codepen\">See the Pen [Complex H2 Query [forked]](https:\/\/codepen.io\/smashingmag\/pen\/zxqxNev) by <a href=\"https:\/\/codepen.io\/bryanrasmussen\">Bryan Rasmussen<\/a>.<\/p><figcaption>See the Pen <a href=\"https:\/\/codepen.io\/smashingmag\/pen\/zxqxNev\">Complex H2 Query [forked]<\/a> by <a href=\"https:\/\/codepen.io\/bryanrasmussen\">Bryan Rasmussen<\/a>.<\/figcaption><\/figure>\n<p>So, yes. There are lots of possible paths to any element in a test using XPath.<\/p>\n<h2 id=\"xslt-1-0-deprecation\">XSLT 1.0 Deprecation<\/h2>\n<p>I mentioned early on that <a href=\"https:\/\/xslt.rip\/\">the Chrome team plans on removing XSLT 1.0 support from the browser<\/a>. That\u2019s important because XSLT 1.0 uses XML-focused programming for document transformation that, in turn, relies on XPath 1.0, which is what is found in most browsers.<\/p>\n<p>When that happens, we\u2019ll lose a key component of XPath. But given the fact that XPath is really great for writing tests, I find it unlikely that XPath as a whole will disappear anytime soon.<\/p>\n<p>That said, I\u2019ve noticed that people get interested in a feature when it\u2019s taken away. And that\u2019s certainly true in the case of XSLT 1.0 being deprecated. <a href=\"https:\/\/news.ycombinator.com\/item?id=45006098\">There\u2019s an entire discussion happening over at Hacker News<\/a> filled with arguments against the deprecation. The post itself is a great example of creating a blogging framework with XSLT. You can read the discussion for yourself, but it gets into how JavaScript might be used as a shim for XLST to handle those sorts of cases.<\/p>\n<p>I have also <a href=\"https:\/\/www.saxonica.com\/saxonjs\/documentation3\/index.html#!browser\">seen suggestions<\/a> that browsers should use SaxonJS, which is a port to JavaScript\u2019s Saxon XSLT, XQUERY, and XPath engines. That\u2019s an interesting idea, especially as Saxon-JS implements the current version of these specifications, whereas there is no browser that implements any version of XPath or XSLT beyond 1.0, and none that implements XQuery.<\/p>\n<p>I reached out to <a href=\"https:\/\/norm.tovey-walsh.com\">Norm Tovey-Walsh<\/a> at Saxonica, the company behind SaxonJS and other versions of the Saxon engine. He said:<\/p>\n<blockquote><p>\u201cIf any browser vendor was interested in taking SaxonJS as a starting point for integrating modern XML technologies into the browser, we\u2019d be thrilled to discuss it with them.\u201d<\/p>\n<p>— <a href=\"https:\/\/norm.tovey-walsh.com\">Norm Tovey-Walsh<\/a><\/p><\/blockquote>\n<p>But also added:<\/p>\n<blockquote><p>\u201cI would be very surprised if anyone thought that taking SaxonJS in its current form and dropping it into the browser build unchanged would be the ideal approach. A browser vendor, by nature of the fact that they build the browser, could approach the integration at a much deeper level than we can \u2018from the outside\u2019.\u201d<\/p>\n<p>— <a href=\"https:\/\/norm.tovey-walsh.com\">Norm Tovey-Walsh<\/a><\/p><\/blockquote>\n<p>It\u2019s worth noting that Tovey-Walsh\u2019s comments came about a week before the XSLT deprecation announcement.<\/p>\n<h2 id=\"conclusion\">Conclusion<\/h2>\n<p>I could go on and on. But I hope this has demonstrated the <strong>power of XPath<\/strong> and given you plenty of examples demonstrating how to use it for achieving great things. It\u2019s a perfect example of older technology in the browser stack that still has plenty of <strong>utility<\/strong> today, even if you\u2019ve never known it existed or never considered reaching for it.<\/p>\n<h3 id=\"further-reading\">Further Reading<\/h3>\n<ul>\n<li>\u201c<a href=\"https:\/\/dl.acm.org\/doi\/full\/10.1145\/3700523.3700536\">Enhancing the Resiliency of Automated Web Tests with Natural Language<\/a>\u201d (ACM Digital Library) by Maroun Ayli, Youssef Bakouny, Nader Jalloul, and Rima Kilany<br \/>\n<em>This article provides many XPath examples for writing resilient tests.<\/em><\/li>\n<li><a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/XML\/XPath\">XPath<\/a> (MDN)<br \/>\n<em>This is an excellent place to start if you want a technical explanation detailing how XPath works.<\/em><\/li>\n<li><a href=\"http:\/\/www.zvon.org\/xxl\/XPathTutorial\/General\/examples.html\">XPath Tutorial<\/a> (ZVON)<br \/>\n<em>I\u2019ve found this tutorial to be the most helpful in my own learning, thanks to a wealth of examples and clear explanations.<\/em><\/li>\n<li><a href=\"https:\/\/xpather.com\">XPather<\/a><br \/>\n<em>This interactive tool lets you work directly with the code.<\/em><\/li>\n<\/ul>\n<div class=\"signature\">\n <img src=\"data:image\/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==\" alt=\"Smashing Editorial\" width=\"35\" height=\"46\" loading=\"lazy\" class=\"lazyload\" data-src=\"https:\/\/www.smashingmagazine.com\/images\/logo\/logo--red.png\"><br \/>\n <span>(gg, yk)<\/span>\n<\/div>\n<\/article>\n","protected":false},"excerpt":{"rendered":"<p>Older Tech In The Browser Stack Older Tech In The Browser Stack Bryan Rasmussen 2025-11-13T08:00:00+00:00 2025-11-13T20:32:27+00:00 I\u2019ve been in front-end development long enough to see a trend over the years: younger developers working with a new paradigm of programming without understanding the historical context of it. It is, of course, perfectly understandable to not know…<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":[],"categories":[11],"tags":[],"_links":{"self":[{"href":"https:\/\/computercoursesonline.com\/index.php\/wp-json\/wp\/v2\/posts\/1107"}],"collection":[{"href":"https:\/\/computercoursesonline.com\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/computercoursesonline.com\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/computercoursesonline.com\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/computercoursesonline.com\/index.php\/wp-json\/wp\/v2\/comments?post=1107"}],"version-history":[{"count":1,"href":"https:\/\/computercoursesonline.com\/index.php\/wp-json\/wp\/v2\/posts\/1107\/revisions"}],"predecessor-version":[{"id":1108,"href":"https:\/\/computercoursesonline.com\/index.php\/wp-json\/wp\/v2\/posts\/1107\/revisions\/1108"}],"wp:attachment":[{"href":"https:\/\/computercoursesonline.com\/index.php\/wp-json\/wp\/v2\/media?parent=1107"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/computercoursesonline.com\/index.php\/wp-json\/wp\/v2\/categories?post=1107"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/computercoursesonline.com\/index.php\/wp-json\/wp\/v2\/tags?post=1107"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}