-
Notifications
You must be signed in to change notification settings - Fork 35
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
v0.1 - get full CSS selector path of any element
- Loading branch information
Joss Crowcroft
committed
Mar 27, 2011
1 parent
f243e27
commit 6e12d2d
Showing
2 changed files
with
168 additions
and
83 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,26 @@ | ||
Simple JavaScript DOM Inspector | ||
=============================== | ||
Simple JavaScript DOM Inspector v0.1 | ||
==================================== | ||
|
||
Really hacked-together, doesn't do much yet, but might be interesting! | ||
Highlights hovered elements with a 2px red outline, and then logs the element's full | ||
CSS selector path when clicked. You can also display the selected element's XPath, if | ||
that's your kinda thing, or do anything with it in the callback function. | ||
|
||
The CSS selector path-getting code tries to be as specific as possible, but could be | ||
cut down to make an optimised selector. It works its way up the element's parentNodes | ||
to build a full jQuery-style selector string with each element's ID and CSS class. | ||
|
||
It also checks to see whether any part of the CSS path matches multiple elements, or if | ||
any element has no ID or CSS class, and adds specific "nth-child" pseudo-selectors | ||
where needed, eg. "p:nth-of-type(3)". | ||
|
||
Hit escape key to cancel the inspector. | ||
|
||
NB: XPath code removed as it didn't really work very well, need to write from scratch. | ||
|
||
Started putting in IE support, but won't work in IE just yet, check back next week | ||
for that (so far, tested in FF4, Chrome, Safari, Opera 11.) | ||
|
||
No warranty; probably won't break the internet. Improvements and linkbacks welcome! | ||
|
||
|
||
- Joss |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,115 +1,180 @@ | ||
/** | ||
* Simple JavaScript DOM Inspector v0.0.1 - Joss Crowcroft | ||
* Simple JavaScript DOM Inspector v0.1 | ||
* | ||
* Hacked together in 20 minutes so no guarantees it'll work, but putting it in a repo as I think it could become quite useful with some tidying up! | ||
* Default functionality is to highlight elements with a 2px red outline, and alert out the element's XPath on click. | ||
* Highlights hovered elements with a 2px red outline, and then logs the element's full | ||
* CSS selector path when clicked. You can also display the selected element's XPath, if | ||
* that's your kinda thing, or do anything with it in the callback function. | ||
* | ||
* Could be configured to display a Sizzle/jQuery selector instead of XPath, or to do pretty much anything. | ||
* | ||
* Started putting in IE support, but won't work in IE just yet, check back next week for that (so far, tested in FF4, Chrome, Safari, Opera 11) | ||
* The CSS selector path-getting code tries to be as specific as possible, but could be | ||
* cut down to make an optimised selector. It works its way up the element's parentNodes | ||
* to build a full jQuery-style selector string with each element's ID and CSS class. | ||
* | ||
* It also checks to see whether any part of the CSS path matches multiple elements, or if | ||
* any element has no ID or CSS class, and adds specific "nth-child" pseudo-selectors | ||
* where needed, eg. "p:nth-of-type(3)". | ||
* | ||
* Hit escape key to cancel the inspector. | ||
* | ||
* NB: XPath code removed as it didn't really work very well, need to write from scratch. | ||
* | ||
* Started putting in IE support, but won't work in IE just yet, check back next week | ||
* for that (so far, tested in FF4, Chrome, Safari, Opera 11.) | ||
* | ||
* No warranty; probably won't break the internet. Improvements and linkbacks welcome! | ||
*/ | ||
|
||
(function(d) { | ||
var last; | ||
|
||
// XPath finder (modified from http://snippets.dzone.com/posts/show/4349) | ||
// JSHinted, but not double-checked for coolness or accuracy (yet): | ||
function getXPath(node, path) { | ||
path = path || []; | ||
var count, sibling; | ||
if(node.parentNode) { | ||
path = getXPath(node.parentNode, path); | ||
(function(document) { | ||
var last; | ||
|
||
/** | ||
* Get full CSS path of any element | ||
* | ||
* Returns a jQuery-style full CSS selector path, including IDs, classes and | ||
* ":nth-of-type()" child pseudo-selectors (when required): | ||
*/ | ||
function cssPath(el) { | ||
var cssPathStr = '', | ||
parents = [], | ||
tagName, | ||
cssId, | ||
cssClass, | ||
vagueMatch, | ||
nth, | ||
i, | ||
c, | ||
tagSelector; | ||
|
||
// Build array of parent nodes: | ||
while ( el.parentNode ) { | ||
parents.push( el ); | ||
el = el.parentNode; | ||
} | ||
if(node.previousSibling) { | ||
count = 1; | ||
sibling = node.previousSibling; | ||
do { | ||
if(sibling.nodeType === 1 && sibling.nodeName === node.nodeName) { | ||
count++; | ||
} | ||
sibling = sibling.previousSibling; | ||
} while(sibling); | ||
if(count === 1) { | ||
count = null; | ||
|
||
// Go down the list of parent nodes and build unique identifier for each: | ||
for ( i = parents.length-1; i >= 0; i-- ) { | ||
el = parents[i]; | ||
vagueMatch = 0; | ||
|
||
// Get the node's tag name, ID and CSS classes: | ||
tagName = el.nodeName.toLowerCase(); | ||
cssId = ( el.id ) ? ( '#' + el.id ) : false; | ||
cssClass = ( el.className ) ? ( '.' + el.className.replace(/ /g,".") ) : ''; | ||
|
||
// Build a unique identifier for this parent node: | ||
if ( cssId ) { | ||
// Matched by ID: | ||
tagSelector = tagName + cssId + cssClass; | ||
} else if ( cssClass ) { | ||
// Matched by class (will be checked for multiples afterwards): | ||
tagSelector = tagName + cssClass; | ||
} else { | ||
// Couldn't match by ID or class, so use ":nth-child()" instead: | ||
vagueMatch = 1; | ||
tagSelector = tagName; | ||
} | ||
} else if(node.nextSibling) { | ||
sibling = node.nextSibling; | ||
do { | ||
if(sibling.nodeType === 1 && sibling.nodeName === node.nodeName) { | ||
count = 1; | ||
sibling = null; | ||
} else { | ||
count = null; | ||
sibling = sibling.previousSibling; | ||
|
||
// Add this tag's CSS selector to CSS path string: | ||
cssPathStr += ' '+tagSelector; | ||
|
||
// If the complete/semi-complete cssPathStr matches multiple child nodes, add ":nth-child()" pseudo-selector to match: | ||
// NB: Only do this if element has no #id and is not <html>/<body>; | ||
// Always do this if element has no CSS class (vague match) | ||
if ( !tagSelector.match(/(html|body)/) && !cssId && ( vagueMatch || $( cssPathStr ).length > 1 ) ) { | ||
el = parents[i]; | ||
nth = 1; | ||
|
||
// Cycle through elements siblings to match same tags for ":nth-of-type" selector: | ||
while ( el = el.previousElementSibling ) { | ||
// Is this the same type of HTML tag as the selected element?: | ||
if ( el.nodeName.toLowerCase() === tagName ) | ||
nth++; | ||
} | ||
} while(sibling); | ||
} | ||
if(node.nodeType === 1) { | ||
path.push(node.nodeName.toLowerCase() + (node.id ? "[@id='"+node.id+"']" : count > 0 ? "["+count+"]" : '')); | ||
|
||
// Append nth-child pseudo-selector to CSS path: | ||
cssPathStr += ":nth-of-type(" + nth + ")"; | ||
} | ||
} | ||
return path; | ||
|
||
// Return full CSS path: | ||
return cssPathStr.replace(' ', ''); | ||
} | ||
|
||
// Mouseover any element, this happens: | ||
|
||
|
||
/** | ||
* MouseOver action for all elements on the page: | ||
*/ | ||
function inspectorMouseOver(e) { | ||
var element = e.target; // NB: not IE (needs fix) | ||
// NB: this doesn't work in IE (needs fix): | ||
var element = e.target; | ||
|
||
element.style.outlineWidth = '2px'; | ||
element.style.outlineStyle = 'solid'; | ||
element.style.outlineColor = '#f00'; | ||
// Set outline: | ||
element.style.outline = '2px solid #f00'; | ||
|
||
// Set last selected element so it can be 'deselected' on cancel. | ||
last = element; | ||
} | ||
|
||
// Mouseout of any element, do this: | ||
|
||
/** | ||
* MouseOut event action for all elements | ||
*/ | ||
function inspectorMouseOut(e) { | ||
e.target.style.outlineStyle = 'none'; | ||
// Remove outline from element: | ||
e.target.style.outline = ''; | ||
} | ||
|
||
// Clicked something? You know what to do: | ||
|
||
/** | ||
* Click action for hovered element | ||
*/ | ||
function inspectorOnClick(e) { | ||
e.preventDefault(); | ||
|
||
/* Maybe: | ||
var selection = e.target.innerHTML; | ||
alert('Element: ' + evt.target.toString() + '\n\nSelection:\n\n' + selection); | ||
*/ | ||
|
||
alert( 'xpath: '+ getXPath( e.target ) ); | ||
// These are the default actions (the XPath code might be a bit janky) | ||
// Really, these could do anything: | ||
console.log( cssPath(e.target) ); | ||
/* console.log( getXPath(e.target).join('/') ); */ | ||
|
||
return false; | ||
} | ||
|
||
// Function to cancel inspector: | ||
function inspectorCancel( e ) { | ||
|
||
|
||
/** | ||
* Function to cancel inspector: | ||
*/ | ||
function inspectorCancel(e) { | ||
// Unbind inspector mouse and click events: | ||
if (e === null && event.keyCode === 27) { // IE (won't work yet): | ||
d.detachEvent("mouseover", inspectorMouseOver); | ||
d.detachEvent("mouseout", inspectorMouseOut); | ||
d.detachEvent("click", inspectorOnClick); | ||
d.detachEvent("keydown", inspectorCancel); | ||
document.detachEvent("mouseover", inspectorMouseOver); | ||
document.detachEvent("mouseout", inspectorMouseOut); | ||
document.detachEvent("click", inspectorOnClick); | ||
document.detachEvent("keydown", inspectorCancel); | ||
last.style.outlineStyle = 'none'; | ||
} else if(e.which === 27) { // Better browsers: | ||
d.removeEventListener("mouseover", inspectorMouseOver, true); | ||
d.removeEventListener("mouseout", inspectorMouseOut, true); | ||
d.removeEventListener("click", inspectorOnClick, true); | ||
d.removeEventListener("keydown", inspectorCancel, true); | ||
document.removeEventListener("mouseover", inspectorMouseOver, true); | ||
document.removeEventListener("mouseout", inspectorMouseOut, true); | ||
document.removeEventListener("click", inspectorOnClick, true); | ||
document.removeEventListener("keydown", inspectorCancel, true); | ||
|
||
// Remove outline on last-selected element: | ||
last.style.outlineStyle = 'none'; | ||
last.style.outline = 'none'; | ||
} | ||
} | ||
|
||
|
||
// Add event listeners for DOM-inspectorey actions | ||
if ( d.addEventListener ) { | ||
d.addEventListener("mouseover", inspectorMouseOver, true); | ||
d.addEventListener("mouseout", inspectorMouseOut, true); | ||
d.addEventListener("click", inspectorOnClick, true); | ||
d.addEventListener("keydown", inspectorCancel, true); | ||
} else if ( d.attachEvent ) { | ||
d.attachEvent("mouseover", inspectorMouseOver); | ||
d.attachEvent("mouseout", inspectorMouseOut); | ||
d.attachEvent("click", inspectorOnClick); | ||
d.attachEvent("keydown", inspectorCancel); | ||
|
||
/** | ||
* Add event listeners for DOM-inspectorey actions | ||
*/ | ||
if ( document.addEventListener ) { | ||
document.addEventListener("mouseover", inspectorMouseOver, true); | ||
document.addEventListener("mouseout", inspectorMouseOut, true); | ||
document.addEventListener("click", inspectorOnClick, true); | ||
document.addEventListener("keydown", inspectorCancel, true); | ||
} else if ( document.attachEvent ) { | ||
document.attachEvent("mouseover", inspectorMouseOver); | ||
document.attachEvent("mouseout", inspectorMouseOut); | ||
document.attachEvent("click", inspectorOnClick); | ||
document.attachEvent("keydown", inspectorCancel); | ||
} | ||
|
||
})(document); |