XPath & CSS Path - Yash-777/SeleniumDriverAutomation GitHub Wiki

Introduction to using XPath in JavaScript

XPath stands for XML Path Language. XPath/locator is a way of addressing an element by navigating through the Tree structure.

document.evaluate(String xpathExpression)

xpathExpression - DOM Address:

  1. Absolute Path(/) - From the root HTML element, by navigating through the Tree structure. /html/body/div[5]/div[4]
  2. Relative Path(//) - Unique address in DOM, parts of an XML document. //*[@id="js-repo-pjax-container"]/div[1]/div[1]/h1/strong/a

This method evaluates XPath expressions against an XML based document (including HTML documents), and returns a XPathResult object, which can be a single node or a set of nodes.

var xpathResult = document.evaluate(
									 xpathExpression, 
									 contextNode, 
									 namespaceResolver, 
									 resultType, 
									 result
									);

Examle from stack & generation of xpath from chrome browser.

function xpathEval(xpathExpression, dom, resultType, color) {
	try {
		var element = document.evaluate(xpathExpression, dom, null, resultType, null).singleNodeValue;
		if( color != null && color != '' ) 
			element.style.setProperty( 'outline', "3px solid "+color,"important");
		return element;
	} catch (e) {
		return null;
	}
}

Console :
var xpathExpression = '//*[@id="js-repo-pjax-container"]/div[1]/div[1]/ul/li[1]/form';
xpathEval(xpathExpression, window.document, 9, 'red');

DOM hierarchical structured tree Traversing: Access always starts from the document. This object provides a variety of methods to search and modify elements. The DOM consists of a hierarchy of nodes where each node can have a parent, a list of child nodes and a nextSibling and previousSibling. from this information we will generate XPath.

Element Siblings

An element is one type of a node. The nodeType property can be used to distinguish different kind of nodes, such that elements, text and comments, from each other.

Node type constants

HTMLElement XPath:

var htmlelement = document.getElementsByTagName('div')[20];
HTMLElement_XPath(htmlelement);
HTMLElement_RelativeXPath(htmlelement);
  • Absolute XPath (/): Ex: /html/body/DIV[5]/DIV[1]/DIV[1]
function HTMLElement_XPath(element) {
    if (element.tagName == 'HTML')    return '/html';
    if (element===document.body)      return '/html/body';

    // calculate position among siblings
    var position = 0;
    // Gets all siblings of that element.
    var siblings = element.parentNode.childNodes;
    for (var i = 0; i < siblings.length; i++) {
        var sibling = siblings[i];
        // Check Siblink with our element if match then recursively call for its parent element.
        if (sibling === element)  return HTMLElement_XPath(element.parentNode)+'/'+element.tagName+'['+(position+1)+']';

       // if it is a siblink & element-node then only increments position. 
        var type = sibling.nodeType;
        if (type === 1 && sibling.tagName === element.tagName)            position++;
    }
}
  • Relative XPath (//) :
function HTMLElement_RelativeXPath(element) {

}

MouseEvent + KeyboardEvent XPath:

The MouseEvent interface represents events that occur due to the user interacting with a pointing device (such as a mouse). MouseEvent derives from UIEvent, which in turn derives from Event. Common events list

KeyboardEvent objects describe a user interaction with the keyboard. Each event describes a key; the event type (keydown, keypress, or keyup) identifies what kind of activity was performed.

We are using some of these listener functions to register events to document. addEventListener, removeEventListener. We can get the list of events through this function getEventListeners(object).

Event Listeners

Based on keycode we are going to generate xpath on mouse event element.

var onMouse_Event = null,onMouse_Tag = null, onMouse_TagName = null, eventXPath = '';
var events = {
	listnerApply : window.document,
	activate : function() {
		if (window.addEventListener) {  // W3C DOM
			events.listnerApply.addEventListener('mousemove', events.MouseMove, false);
			events.listnerApply.addEventListener('mouseout', events.MouseOut, false);
			events.listnerApply.addEventListener('keydown', events.KeyDowns, false);
		}
	},
	deActivate : function() {
		if (window.removeEventListener) {  // W3C DOM
			events.listnerApply.removeEventListener('mousemove', events.MouseMove, false);
			events.listnerApply.removeEventListener('mouseout', events.MouseOut, false);
			events.listnerApply.removeEventListener('keydown', events.KeyDown, false);
		}
	},
		
	MouseMove : function( MouseEvent ) {
		onMouse_Event = MouseEvent;
		onMouse_Tag = MouseEvent.target || MouseEvent.srcElement || MouseEvent.toElement;
		onMouse_TagName = onMouse_Tag.nodeName || onMouse_Tag.tagName;
		onMouse_Tag.style.setProperty ('border', '1px solid #0099cc', 'important');
	},
	
	MouseOut : function ( MouseEvent ) {
		onMouse_Event = MouseEvent;
		onMouse_Tag.style.setProperty ('border', '0px', 'important');
	},	
	KeyDowns : function( KeyboardEvent ) {
		keyDownCode = KeyboardEvent.keyCode || KeyboardEvent.which;
				
		if( keyDownCode == 16 || keyDownCode == 18 ) { // SHIFT - 16, ALT - 18
			MouseEvent_XPath( onMouse_Event );	onMouse_Event.preventDefault();
		}		
		if( keyDownCode == 17 ) { // CTRL key value 17
			events.deActivate();
		}
	}	
};
events.activate();

function MouseEvent_XPath(e) {
	event = e.target || e.srcElement ||e.originalTarget;

	for ( var xpathEle = ''; event && event.nodeType == 1; event = event.parentNode ) {
		if ( event.tagName == 'HTML' || event.tagName == 'html' ) {
			eventXPath = '//html'+xpathEle;		break;
		}     
		if ( event.tagName == 'BODY' || event.tagName == 'body' ) {
			eventXPath = '//html/body'+xpathEle;	break;
		}

		// calculate position among siblings
		var position = 0;
		// Gets all siblings of that event.
		var siblings = event.parentNode.children;
		for (var i = 0; i < siblings.length; i++) {
			var sibling = siblings[i];
			// Check Sibling with our event if match then recursively call for its parent event.
			if (sibling === event)  xpathEle = "/"+event.tagName+'['+(position+1)+']'+xpathEle;

			// if it is a sibling with same tagName then only increments position.
			if (sibling.tagName === event.tagName)		position++;
		}
	}
	console.log('MouseEvent + KeyboardEvent XPath: ', eventXPath);
	return eventXPath;
}

CSS Path

CSS locator work with simple attributes like ID and Class. To get the unique|absolute CSS Path to a DOM-element as a CSS selector|locator use below function.

Example Code:

function getFullCSSPath(element) {
    if (element.tagName == 'HTML')    return 'html';
      
    var siblings = element.parentNode.childNodes;
    for (var i = 0; i < siblings.length; i++) {
        var sibling = siblings[i];
        if (sibling === element)    {
        	var elemCssPath = getShortCSSPath(element);
      		return getFullCSSPath(element.parentNode)+' '+elemCssPath; 
        }
    }
}
function getShortCSSPath(element) {
	var path = element.tagName.toLowerCase();
	if(element.id) {
		path += "#" + (element.id).replace(/\s+/g,"#");	
	}		
	if(element.className) {
		path += "." + (element.className).replace(/\s+/g,".");
	}		
	return path;
}

console.log( getShortCSSPath(document.getElementsByTagName('div')[20]) ); // div#js-repo-pjax-container
console.log( getFullCSSPath(document.getElementsByTagName('div')[20]) );
// html body.logged-in.env-production.windows.vis-public div div div#js-repo-pjax-container