forked from muka/Simple-JavaScript-DOM-Inspector
-
Notifications
You must be signed in to change notification settings - Fork 0
/
inspector.js
198 lines (163 loc) · 6.4 KB
/
inspector.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
/**
* Simple JavaScript DOM Inspector v0.1.2
*
* Highlights hovered elements with a 2px red outline, and then provides the hovered element
* on click to the callback function, which can do anything with it.
*
* By default, the inspector just console.logs a jQuery-style CSS selector path for the element.
*
* The CSS path-building code works up the given element's parent nodes to create an optimised
* selector string, stopping at the first parent with an #id, but can also create an
* ultra-specific full CSS path, right down to 'html'.
*
* Optionally, it can check to see if any part of the CSS path matches multiple elements, or if
* any element in the CSS path has no #id or .class, and add specific ":nth-child()"
* pseudo-selector to match the element, eg. "html body #content p img:nth-child(1)"
*
* 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
*/
(function(document) {
var last;
/**
* Get full CSS path of any element
*
* Returns a jQuery-style CSS path, with IDs, classes and ':nth-child' pseudo-selectors.
*
* Can either build a full CSS path, from 'html' all the way to ':nth-child()', or a
* more optimised short path, stopping at the first parent with a specific ID,
* eg. "#content .top p" instead of "html body #main #content .top p:nth-child(3)"
*/
function cssPath(el) {
var fullPath = 0, // Set to 1 to build ultra-specific full CSS-path, or 0 for optimised selector
useNthChild = 0, // Set to 1 to use ":nth-child()" pseudo-selectors to match the given element
cssPathStr = '',
testPath = '',
parents = [],
parentSelectors = [],
tagName,
cssId,
cssClass,
tagSelector,
vagueMatch,
nth,
i,
c;
// Go up the list of parent nodes and build unique identifier for each:
while ( el ) {
vagueMatch = 0;
// Get the node's HTML tag name in lowercase:
tagName = el.nodeName.toLowerCase();
// Get node's ID attribute, adding a '#':
cssId = ( el.id ) ? ( '#' + el.id ) : false;
// Get node's CSS classes, replacing spaces with '.':
cssClass = ( el.className ) ? ( '.' + el.className.replace(/\s+/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;
}
// Add this full tag selector to the parentSelectors array:
parentSelectors.unshift( tagSelector )
// If doing short/optimised CSS paths and this element has an ID, stop here:
if ( cssId && !fullPath )
break;
// Go up to the next parent node:
el = el.parentNode !== document ? el.parentNode : false;
} // endwhile
// Build the CSS path string from the parent tag selectors:
for ( i = 0; i < parentSelectors.length; i++ ) {
cssPathStr += ' ' + parentSelectors[i];// + ' ' + cssPathStr;
// If using ":nth-child()" selectors and this selector has no ID / isn't the html or body tag:
if ( useNthChild && !parentSelectors[i].match(/#/) && !parentSelectors[i].match(/^(html|body)$/) ) {
// If there's no CSS class, or if the semi-complete CSS selector path matches multiple elements:
if ( !parentSelectors[i].match(/\./) || $( cssPathStr ).length > 1 ) {
// Count element's previous siblings for ":nth-child" pseudo-selector:
for ( nth = 1, c = el; c.previousElementSibling; c = c.previousElementSibling, nth++ );
// Append ":nth-child()" to CSS path:
cssPathStr += ":nth-child(" + nth + ")";
}
}
}
// Return trimmed full CSS path:
return cssPathStr.replace(/^[ \t]+|[ \t]+$/, '');
}
/**
* MouseOver action for all elements on the page:
*/
function inspectorMouseOver(e) {
// NB: this doesn't work in IE (needs fix):
var element = e.target;
// Set outline:
element.style.outline = '2px solid #f00';
// Set last selected element so it can be 'deselected' on cancel.
last = element;
}
/**
* MouseOut event action for all elements
*/
function inspectorMouseOut(e) {
// Remove outline from element:
e.target.style.outline = '';
}
/**
* Click action for hovered element
*/
function inspectorOnClick(e) {
e.preventDefault();
// 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) {
// Unbind inspector mouse and click events:
if (e === null && event.keyCode === 27) { // IE (won't work yet):
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:
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.outline = 'none';
}
}
/**
* 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);