1

I created a simple event listener for clicks:

window.addEventListener("click", function (event) {
  console.log(event);
});

From what I see, the event object contains a lot of useful data about the parent elements, HTML and CSS data of the clicked element.

Is there a way to build a CSS-selector (hopefully unique) from these event objects? If yes, are there any open-source solutions you can think of?

t.niese
  • 39,256
  • 9
  • 74
  • 101
Andriy Stolyar
  • 585
  • 9
  • 21
  • 1
    *"Is there a way to build a CSS-selector (hopefully unique) from these event objects?"* Yes, you can build a unique CSS selector for any element if you start with a reference to the element (such as `event.target`). That selector is not likely to be *useful*, however. Without knowing what you want to do with it, we can't reasonably answer this question. – T.J. Crowder Oct 18 '17 at 14:15
  • 1
    I want to record user actions on my website using eventListenters, and then replay these actions using Selenium(browser automation). I was going to use CSS-selectors of elements to find them and click(there's a function in Selenium called find_element_by_css_selector()). – Andriy Stolyar Oct 18 '17 at 14:20
  • You can walk the DOM up to the top and create a selector such as `body div:nth-child(44) span:nth-child(3)`. –  Oct 18 '17 at 14:34
  • You could save the x/y of the event then [dispatch a click event at that x/y coordinate](https://stackoverflow.com/a/3277417/2033671) –  Oct 18 '17 at 14:37

1 Answers1

3

Yes, it's entirely possible to build a unique CSS selector for any element in the DOM, because of the pseudo-class :nth-child which lets us differentiate between two elements with the same characteristics in the same parent.

Here's a simple example, which builds a selector using the tag name and its position relative to other elements within its parent. This example builds and shows the selector, then uses it a quarter second later to add a 'clicked' class to the element (which shows it in bold green):

// Find the index of the given element in its parent
function indexOf(element) {
    var parent = element.parentNode;
    var child, index = 1;
    for (child = parent.firstElementChild;
         child;
         child = child.nextElementSibling) {
        if (child === element) {
            return index;
        }
        ++index;
    }
    return -1;
}
document.addEventListener("click", function(e) {
    // Starting from this element, build a tagname:nth-child(x) selector
    // for it, then prepend one for each of its parents up to BODY
    var element = e.target;
    var selector = element.tagName + ":nth-child(" + indexOf(element) + ")";
    while ((element = element.parentElement) != null) {
        if (element.tagName === "BODY") {
            selector = "BODY > " + selector;
            break;
        }
        selector = element.tagName + ":nth-child(" + indexOf(element) + ") > " + selector;
    }
    show(selector);
});
function show(selector) {
    console.log(selector);
    setTimeout(function() {
        document.querySelector(selector).classList.add("clicked");
    }, 250);
}
#container, #container div {
  border: 1px solid #ddd;
}
.clicked {
  color: green;
  font-weight: bold;
}
<div id="container">
  <div>
    <span>one</span>
    <span>two</span>
    <span>three</span>
    <span>four</span>
  </div>
  <div>
    <span>one</span>
    <span>two</span>
    <span>three</span>
    <span>four</span>
  </div>
</div>
T.J. Crowder
  • 1,031,962
  • 187
  • 1,923
  • 1,875