There are many javask-based design patterns and algorithms in the front-end domain, and they play an important role in many complex applications. The following is a look at the interpreter pattern in the javascript design pattern, and use it to implement an algorithm that gets an element’s Xpath path.

Review of the previous period

  • N+1 application scenarios of the iterator pattern in Front End Combat Summary
  • The Application of design Pattern of “Summary of Front End Actual Combat” — Memorandum Pattern

The body of the

1. Interpreter mode

For a language, we give it a grammatical representation (a syntactic description tool in a language that defines the rules of the language), and we define an interpreter by which the sentences defined in the language are interpreted.

Definition may sound abstract, for example, for our common website multi-language, to achieve multi-language, we first need to pre-define the language type, design the different language corpus in advance, and then we will map to different languages according to the configuration and uniform variable rules.

2. The Xpath path of the element

XPath is used to navigate through elements and attributes in an XML document. Although XPath is used to find XML nodes, it can also be used to find nodes in HTML documents, since HTML and XML have similar structures. Here we only consider HTML, the path of the element in the HTML page.

So how do I quickly get an element’s Xpath path? It’s really simple. Let’s go to Google Debugger:

//*[@id="juejin"]/div[2]/main/div/div[1]/article/div[1]
Copy the code

There are many application scenarios to obtain the Xpath path of elements. For example, we often use Python crawler. The crawler framework can easily control a DOM node in the page through the Xpath path, and then obtain the desired data and elements. For example, we send the Xpath path of the element to the back end, and the back end can count the usage and interactive data of a certain function. For example, the analysis of users in the site to browse the thermal distribution map, path portrait and so on.

3.js implementation to get the element’s Xpath path

For example, if we have a page, the span element has the following structure:


      
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="Width = device - width, initial - scale = 1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
</head>
<body>
    <div>
       <span>I'm Xu Xiaoxi</span>
    </div>
</body>
</html>
Copy the code

Then our Xpath path might look like this:

HTML/BODY|HEAD/DIV/SPAN
Copy the code

As you can see, our rightmost element is the target element, and the leftmost element is the outermost container. To do this, we first get the parent element of the current element through the parentNode of the element until we find the topmost position. However, we also need to note that every time we find the previous level, we also need to iterate over the previousSibling element that precedes the element. If the sibling element has the same name as the element after it, we will add 1 to the element name.

The first step is to implement a method getSameLevelName that iterates over sibling elements:

// Get the sibling element name
function getSameLevelName(node){
    // If there are sibling elements
    if(node.previousSibling) {
        let name = ' '.// Return the sibling element name string
           count = 1.// Number of elements with the same name in adjacent sibling elements
           nodeName = node.nodeName,
           sibling = node.previousSibling;
        while(sibling){
            if(sibling.nodeType == 1 && sibling.nodeType === node.nodeType && sibling.nodeName){
                if(nodeName == sibling.nodeName){
                    name += ++count;
                }else {
                    // Rework the number of nodes with the same name as the next node
                    count = 1;
                    // Append the new node name
                    name += '|' + sibling.nodeName.toUpperCase()
                }
            }
            sibling = sibling.previousSibling;
        }
        return name
    }else {
        // There is no sibling element
        return ' '}}Copy the code

The second step is to traverse the document tree.

// The XPath interpreter
let Interpreter = (function(){
    return function(node, rwrap){
        // Array of paths
        let path = [],
        // If no container node exists, the default is document
        wrap = rwrap || document;
        // If the current node equals the container node
        if(node === wrap) {
            if(wrap.nodeType == 1) {
                path.push(wrap.nodeName.toUpperCase())
            }
            return path
        }
        // If the parent of the current node is not equal to the container node
        if(node.parentNode ! == wrap){// Iterate over the parent node of the current node
            path = arguments.callee(node.parentNode, wrap)
        }
        // If the parent element of the current node is the same as the container node
        else {
            wrap.nodeType == 1 && path.push(wrap.nodeName.toUpperCase())
        }
        // Get the name statistics of the sibling elements of an element
        let siblingsNames = getSameLevelName(node)
        if(node.nodeType == 1){
            path.push(node.nodeName.toUpperCase() + siblingsNames)
        }
        // Return the final result of the path array
        return path
    }
})()
Copy the code

With these two methods, we can easily obtain the XPath path of the element, such as:

let path = Interpreter(document.querySelector('span'))
console.log(path.join('/'))
Copy the code

This will return the same data structure as at the beginning. Such as: HTML/BODY | HEAD/DIV/SPAN

The last

If you want to learn more about webpack, node, gulp, CSS3, javascript, nodeJS, Canvas and other front-end knowledge and actual practice, welcome to join us in the public account “Interesting Talk front-end” to learn and discuss, and jointly explore the boundary of the front-end.

More recommended

  • “Front-end combat summary” using CSS3 to achieve cool 3D rotation perspective
  • Add a loading progress bar to your site using pace. Js
  • The Application of design Pattern of “Summary of Front End Actual Combat” — Memorandum Pattern
  • “Front End Combat Summary” using postMessage to achieve pluggable cross-domain chatbot
  • “Front-end combat summary” of the variable promotion, function declaration promotion and variable scope detailed explanation
  • “Front-end combat summary” how to change the URL without refreshing the page
  • A picture shows you how to play vue-Cli3 quickly
  • Vue Advanced Advanced series – Play with Vue and vuex in typescript
  • Implementing a CMS full stack project from 0 to 1 based on nodeJS (Part 1)
  • Implementing a CMS full stack project from 0 to 1 based on nodeJS (middle)
  • Implement a CMS full stack project from 0 to 1 based on nodeJS (Part 2)
  • Write a mock data server using nodeJS in 5 minutes
  • With CSS3 to achieve stunning interviewers background that background animation (advanced source)
  • Teach you to use 200 lines of code to write a love bean spell H5 small game (with source code)
  • Cartesian product is implemented and applied in javascript