├── LICENSE
├── README.md
└── jquery.dom-outline-1.0.js
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License.
2 |
3 | Copyright (c) 2013 Andrew Childs
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the 'Software'), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Dom Outline
2 |
3 | Firebug/Dev Tools-like DOM outline implementation using jQuery.
4 |
5 | ### Example Usage
6 |
7 | ```js
8 | var myExampleClickHandler = function (element) { console.log('Clicked element:', element); }
9 | var myDomOutline = DomOutline({ onClick: myExampleClickHandler, filter: 'div' });
10 |
11 | // Start outline:
12 | myDomOutline.start();
13 |
14 | // Stop outline (also stopped on escape/backspace/delete keys):
15 | myDomOutline.stop();
16 | ```
17 |
18 | ### Options
19 |
20 |
21 |
22 | Option |
23 | Description |
24 | Default |
25 |
26 |
27 | borderWidth |
28 | The width of the outline border, in pixels. |
29 | 2 |
30 |
31 |
32 | onClick |
33 | The function fired when the user clicks while the DOM outline is active. Receives the target element as an argument. |
34 | false |
35 |
36 |
37 | namespace |
38 | The private namespace used for CSS selectors and events. Available in the unlikely event of possible event/CSS collisions. |
39 | 'DomOutline' |
40 |
41 |
42 | filter |
43 | A selector that an element should match in order to be outlined and clicked. By default no filter is applied. |
44 | false |
45 |
46 |
47 |
48 | ### Other Notes
49 |
50 | * Tested to work in Chrome, FF, Safari. Buggy in IE ;(
51 | * Creates a single global variable: `window.DomOutline`
52 |
--------------------------------------------------------------------------------
/jquery.dom-outline-1.0.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Firebug/Web Inspector Outline Implementation using jQuery
3 | * Tested to work in Chrome, FF, Safari. Buggy in IE ;(
4 | * Andrew Childs
5 | *
6 | * Example Setup:
7 | * var myClickHandler = function (element) { console.log('Clicked element:', element); }
8 | * var myDomOutline = DomOutline({ onClick: myClickHandler, filter: '.debug' });
9 | *
10 | * Public API:
11 | * myDomOutline.start();
12 | * myDomOutline.stop();
13 | */
14 | var DomOutline = function (options) {
15 | options = options || {};
16 |
17 | var pub = {};
18 | var self = {
19 | opts: {
20 | namespace: options.namespace || 'DomOutline',
21 | borderWidth: options.borderWidth || 2,
22 | onClick: options.onClick || false,
23 | filter: options.filter || false
24 | },
25 | keyCodes: {
26 | BACKSPACE: 8,
27 | ESC: 27,
28 | DELETE: 46
29 | },
30 | active: false,
31 | initialized: false,
32 | elements: {}
33 | };
34 |
35 | function writeStylesheet(css) {
36 | var element = document.createElement('style');
37 | element.type = 'text/css';
38 | document.getElementsByTagName('head')[0].appendChild(element);
39 |
40 | if (element.styleSheet) {
41 | element.styleSheet.cssText = css; // IE
42 | } else {
43 | element.innerHTML = css; // Non-IE
44 | }
45 | }
46 |
47 | function initStylesheet() {
48 | if (self.initialized !== true) {
49 | var css = '' +
50 | '.' + self.opts.namespace + ' {' +
51 | ' background: #09c;' +
52 | ' position: absolute;' +
53 | ' z-index: 1000000;' +
54 | '}' +
55 | '.' + self.opts.namespace + '_label {' +
56 | ' background: #09c;' +
57 | ' border-radius: 2px;' +
58 | ' color: #fff;' +
59 | ' font: bold 12px/12px Helvetica, sans-serif;' +
60 | ' padding: 4px 6px;' +
61 | ' position: absolute;' +
62 | ' text-shadow: 0 1px 1px rgba(0, 0, 0, 0.25);' +
63 | ' z-index: 1000001;' +
64 | '}';
65 |
66 | writeStylesheet(css);
67 | self.initialized = true;
68 | }
69 | }
70 |
71 | function createOutlineElements() {
72 | self.elements.label = jQuery('').addClass(self.opts.namespace + '_label').appendTo('body');
73 | self.elements.top = jQuery('').addClass(self.opts.namespace).appendTo('body');
74 | self.elements.bottom = jQuery('').addClass(self.opts.namespace).appendTo('body');
75 | self.elements.left = jQuery('').addClass(self.opts.namespace).appendTo('body');
76 | self.elements.right = jQuery('').addClass(self.opts.namespace).appendTo('body');
77 | }
78 |
79 | function removeOutlineElements() {
80 | jQuery.each(self.elements, function(name, element) {
81 | element.remove();
82 | });
83 | }
84 |
85 | function compileLabelText(element, width, height) {
86 | var label = element.tagName.toLowerCase();
87 | if (element.id) {
88 | label += '#' + element.id;
89 | }
90 | if (element.className) {
91 | label += ('.' + jQuery.trim(element.className).replace(/ /g, '.')).replace(/\.\.+/g, '.');
92 | }
93 | return label + ' (' + Math.round(width) + 'x' + Math.round(height) + ')';
94 | }
95 |
96 | function getScrollTop() {
97 | if (!self.elements.window) {
98 | self.elements.window = jQuery(window);
99 | }
100 | return self.elements.window.scrollTop();
101 | }
102 |
103 | function updateOutlinePosition(e) {
104 | if (e.target.className.indexOf(self.opts.namespace) !== -1) {
105 | return;
106 | }
107 | if (self.opts.filter) {
108 | if (!jQuery(e.target).is(self.opts.filter)) {
109 | return;
110 | }
111 | }
112 | pub.element = e.target;
113 |
114 | var b = self.opts.borderWidth;
115 | var scroll_top = getScrollTop();
116 | var pos = pub.element.getBoundingClientRect();
117 | var top = pos.top + scroll_top;
118 |
119 | var label_text = compileLabelText(pub.element, pos.width, pos.height);
120 | var label_top = Math.max(0, top - 20 - b, scroll_top);
121 | var label_left = Math.max(0, pos.left - b);
122 |
123 | self.elements.label.css({ top: label_top, left: label_left }).text(label_text);
124 | self.elements.top.css({ top: Math.max(0, top - b), left: pos.left - b, width: pos.width + b, height: b });
125 | self.elements.bottom.css({ top: top + pos.height, left: pos.left - b, width: pos.width + b, height: b });
126 | self.elements.left.css({ top: top - b, left: Math.max(0, pos.left - b), width: b, height: pos.height + b });
127 | self.elements.right.css({ top: top - b, left: pos.left + pos.width, width: b, height: pos.height + (b * 2) });
128 | }
129 |
130 | function stopOnEscape(e) {
131 | if (e.keyCode === self.keyCodes.ESC || e.keyCode === self.keyCodes.BACKSPACE || e.keyCode === self.keyCodes.DELETE) {
132 | pub.stop();
133 | }
134 |
135 | return false;
136 | }
137 |
138 | function clickHandler(e) {
139 | pub.stop();
140 | self.opts.onClick(pub.element);
141 |
142 | return false;
143 | }
144 |
145 | pub.start = function () {
146 | initStylesheet();
147 | if (self.active !== true) {
148 | self.active = true;
149 | createOutlineElements();
150 | jQuery('body').on('mousemove.' + self.opts.namespace, updateOutlinePosition);
151 | jQuery('body').on('keyup.' + self.opts.namespace, stopOnEscape);
152 | if (self.opts.onClick) {
153 | setTimeout(function () {
154 | jQuery('body').on('click.' + self.opts.namespace, function(e){
155 | if (self.opts.filter) {
156 | if (!jQuery(e.target).is(self.opts.filter)) {
157 | return false;
158 | }
159 | }
160 | clickHandler.call(this, e);
161 | });
162 | }, 50);
163 | }
164 | }
165 | };
166 |
167 | pub.stop = function () {
168 | self.active = false;
169 | removeOutlineElements();
170 | jQuery('body').off('mousemove.' + self.opts.namespace)
171 | .off('keyup.' + self.opts.namespace)
172 | .off('click.' + self.opts.namespace);
173 | };
174 |
175 | return pub;
176 | };
177 |
--------------------------------------------------------------------------------