How Element Searching Works
Basics of Search Element Algorithm
The algorithm consists of two phases.
- Filtering - Search document for Elements that could be the wanted element. We call them candidates.
- Select Winner - Evaluate candidates and pick the best one.
For both phases are used objects named Element Matchers. For the first phase there are Hard Matchers. Hard Matchers search a page of HTML Elements that fits its conditions. The intersection of Hard Matcher elements are passed as candidates to the second phase.
In the second phase, there are Soft Matchers that evaluate each of the candidates with a score. Candidate with the highest score is selected as the best.
If any of Hard Matchers did not find any element, then the algorithm ends with a negative result: Element not found.
If candidates contain only one element, then the algorithm ends: Element found.
Matchers and Identikit
Newired Overlay contains a set of Element Matchers that perform different kinds of evaluation. For instance, there is an XPATH matcher that searches for elements using its XPATH.
Different HTML elements can be successfully found by using different combinations of Matchers. Which Matcher should be used as Hard and which one as Soft.
When an Author, during creation of Journey, selects HTML Element for Journey Step then the Newired Editor creates an entity named Identikit. Identikit consists of list Element Matchers, including configuration which Matchers should be used as Hard or Soft.
Decision which Matcher should be used is made by the Newired implementation. But still there are cases when Author has to tweak the usage of Matchers for a particular Element.
Custom Matchers
In real world web applications, there are situations where Element Matchers embedded as a part of Newired Overlay are still not enough. For those situations, there are Custom Matchers - Matchers that can be implemented by 3rd parties and imported into Newired as a plugin.
How to Customize Element Searching
Custom Element Matcher
Element Matcher is not only a set of conditions for finding or matching HTML Element, but also code that creates these conditions that are stored in Identikit.
How to Define Custom Matcher
Custom Element Matcher has to be implemented as a Newired Plugin that consists of
- Manifest (manifest.json) that defines name, description, version, etc.
- Implementation that consists of one or more JavaScript files.
manifest.json:
{ "id": "customMatcher", "name": "Custom Element Mather", "version": "1.0.0", "extensionPoints": [ { "id": "elementSearch.hardsoft.elementMatcher@1.0.0", "main": "main.js" } ] }
Note: Plugin declares that implements extension point elementSearch.hardsoft.elementMatcher. This means that it contains appropriate functions that extend particular Overlay features. One plugin can implement more extension points.
main.js:
const create = (context, rootElement, element) => {
// return matcher's metadata
}
const evaluateHard = (context, metadata, rootElement) => {
// return list of candidate elements
}
const evaluateSoft = (context, metadata, rootElement, candidates) => {
// return list of evaluated elements
}
const getDescription = (context, conditionId, conditionData) => {
// return object describing given condition
return {
title: "",
description: "",
documentationUrl: ""
}
}
Implementation of Element Matcher itself is a object that consists of functions:
create()
Function create() is responsible for the creation of matcher conditions that will be added into an Identikit.
Matcher condition stored in an Identikit consists of:
- matcherId - Unique identifier of Matcher that will be used to evaluate this condition.
- id - Id of a particular condition. Can be used by the Editor to ask Custom Matcher plugin for description of condition that is used in UI (Editor's Selector Precision Dialog).
matcher.getDescription(conditionId, conditionData)
- usage - Define how a condition will be used by element search algorithm. Values are HARD or SOFT.
- data - Information about an HTML Element stored by Matcher. Content and structure of this field are defined by a Matcher.
Example of create() function that creates conditions for Elements with src attribute with the same value.
const create = (context, rootElement, element) => {
const srcValue = element.getAttribute('src')
if (srcValue) {
return [{
matcherId: 'myMatcher',
id: 'src',
usage: 'HARD',
enabled: true,
data: { value: srcValue }
}]
} else {
return []
}
}
evaluateHard()
Function that uses Matcher conditions marked as Hard to find candidate Element in the Web page.
Example of evaluateHard() function that returns all HTML elements with src attribute that contains text stored in Identikit.
Function that uses Matcher conditions marked as Soft to match them against given candidate elements.
Example of elementSoft() function that returns a list of evaluated candidates. Evaluation is represented as a number named weight.
const evaluateSoft = (context, rootElement, data, candidates) => {
return candidates
.map(element => (
{
element: element,
weight: element.getAttribute('src') === data.value ? 1 : 0
}
))
}
getDescription()
Function that takes a particular Matcher condition and return description to be used in the Editor's Selector Precision Dialog.
Description:
- title - Main title for Matcher
- description - Text shown in the tooltip.
- documentationUrl - URL to external documentation for a particular Matcher.
Values are used in Editor's Selector Precision Dialog in the following way.
Example of getDescription() function.
const getDescription = (context, conditionId, conditionData) => { return { title: 'My SRC condition (' + conditionId + ')', description: 'Element attribute "' + conditionId + '" equals to "' + conditionData.value + '".', documentationUrl: 'https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes', } }
Debugging
For development of Custom Element Matcher or tracking Element search algorithm can be useful to enable detailed logging. This can be via Newired Overlay API.
Open Web browser DevTool and execute in console following command:
newired.userStorage.setLogging("elementFinder", "DEBUG")
Configuration of Custom Matcher
Element Matchers can be configured by properties in namespace derived from the implemented extension point.
elementSearch.hardsoft.elementMatcher.<matcher-name> |
Example of Custom Matcher properties defined in the newired.properties:
newired.elementsearch.hardsoft.elementMatcher.myMatcher.myProperty1 = "Hello" newired.elementsearch.hardsoft.elementMatcher.myMatcher.myProperty2 = 42 newired.elementsearch.hardsoft.elementMatcher.anotherMatcher.myProperty1 = "Hello" |
Element Matcher code can read its properties from the Context object that is passed to all functions.
const create = (context, rootElement, element) => { const property1 = context.get("elementSearch.hardsoft.elementMatcher.myMatcher.myProperty1").value } |
How to import Custom Matcher
Custom Matcher as Newired Plugin is a simple folder with manifest.json and JavaScript files.
myMatcher/
├── main.js
└── manifest.json
Copy this folder into the Portal's plugins folder. Path to this folder is defined in Newired/tomcat/conf/newired.properties by property newired.pluginsPath.
By default it is Newired/plugins.
Then Plugins have to be reloaded from the Portal Administration page. Plugins are also reloaded during Portal startup.
The Portal Administration page also allows you to disable a Plugin. When Custom Matcher is disabled, then it's not used by the Editor. Which means
- Newly created Identikits will not contain disabled Matcher.
- Identikits that already contain disabled Matcher will not use this during element searching.
Hard Matchers Order
During searching for candidates by hard matchers, there are situations when a wanted HTML Element isn't on the Web page at all. In those cases, it's needed to find out this information as quickly as possible.
So Hard Matchers that process its searching quickly, should be performed first. When even one Hard matcher did not find any element, then searching ended with the result: Element not found.
Hard Matchers Order can be defined property elementsearch.hardsoft.hardMatchers in newired.properties.
e.g.:
elementsearch.hardsoft.hardMatchers = elementId, attributeSelector, ..
How to identify Hard Matchers
There are two kinds of Matchers: Embedded and Custom. Embedded Matchers are part of the Newired Overlay provided by Newired. Custom Matchers can be implemented by 3rd parties and added into the Newired Overlay as a Plugin.
Each Matcher has an id that is used in the hard matchers order list. Follow list embedded matchers:
Behavior
Not defined Hard Matchers order
When the list of hardMatchers is not defined, or it's empty then all hard matchers are evaluated in default order.
- Custom Matchers
- Embedded Matchers
- Element Id
- XPATH
- CSS
- Element Attributes
- Element Text
Uncomplete Hard Matchers order
When the list is defined but doesn't contain all matchers.
- Enlisted matchers are evaluated as first in order defined by the list.
- All other matchers are evaluated after in default order.
Example:
elementsearch.hardsoft.hardMatchers = myMatcher, attributeSelector
Actual Order:
- My Matcher
- Embedded Matchers
- Element Attributes
- Element Id
- XPATH
- CSS
- Element Text
Complete Hard Matchers order
When the list is defined and contains all matchers. Then Matchers are evaluated in defined order.
Examples
Example of Custom Attribute Matcher
Follows full example of Custom Matcher that finds and evaluates HTML Element by attribute src.
Element match if has attribute src with value that contains selected element's src ignoring URL parameters.
manifest.json
{
"id": "mySrcAttribute",
"name": "My SRC Attribute Matcher",
"version": "1.0.0",
"extensionPoints": [{
"id": "elementSearch.hardsoft.elementMatcher@1.0.0",
"main": "main.js"
}]
}
main.js:
// // Function creates condition information to be inserted into the Identikit // This implementation stores the value of element attribute 'src' if any. // const create = (context, rootElement, element) => { const srcValue = element.getAttribute('src') if (srcValue) { return [{ matcherId: 'mySrcMatcher', id: 'src', usage: 'HARD', enabled: true, data: { value: srcValue.split('?')[0] } }] } else { return [] } } // // Function returns elements found by information stored in Identikit // This implementation find elements with expected value of attribute 'src' // const evaluateHard = (context, rootElement, data) => { return Array.from(rootElement.querySelectorAll('*[src]')) .filter(e => e.getAttribute('src').indexOf(data.value) !== -1) } // // Function returns list of evaluated candidates. // const evaluateSoft = (context, rootElement, data, candidates) => { return candidates .map(element => ( { element: element, weight: element.getAttribute('src').indexOf(data.value) !== -1 ? 1 : 0 } )) } const getDescription = (context, conditionId, conditionData) => { return { title: 'My SRC condition (' + conditionId + ')', description: 'Element attribute "' + conditionId + '" has to contains.', documentationUrl: 'https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes', } } |
Was this article helpful?
That’s Great!
Thank you for your feedback
Sorry! We couldn't be helpful
Thank you for your feedback
Feedback sent
We appreciate your effort and will try to fix the article