diff --git a/README.md b/README.md index 96182fd..7ec91b2 100644 --- a/README.md +++ b/README.md @@ -38,6 +38,21 @@ myDomOutline.stop(); The private namespace used for CSS selectors and events. Available in the unlikely event of possible event/CSS collisions. 'DomOutline' + + border + A border used as the visual indicator surrounding the element. If false a semi-transparent blue overlay box is used instead (like firebug). + false + + + realtime + Shows the visual indicator as you hover the mouse over the elements. If false the visual indicator shows when clicking an element (maybe implementing ctrl-left click is better?). + false + + + label + Shows a label above the visual indicator. The label contains the element's name, id, class name, and dimensions. + false + ### Other Notes diff --git a/jquery.dom-outline-1.0.js b/jquery.dom-outline-1.0.js index 45ee722..ef13c8b 100644 --- a/jquery.dom-outline-1.0.js +++ b/jquery.dom-outline-1.0.js @@ -1,3 +1,8 @@ +/*global document:false*/ +/*global window:false*/ +/*global jQuery:false*/ +/*global setTimeout:false*/ + /** * Firebug/Web Inspector Outline Implementation using jQuery * Tested to work in Chrome, FF, Safari. Buggy in IE ;( @@ -12,24 +17,29 @@ * myDomOutline.stop(); */ var DomOutline = function (options) { + 'use strict'; + options = options || {}; - var pub = {}; - var self = { - opts: { - namespace: options.namespace || 'DomOutline', - borderWidth: options.borderWidth || 2, - onClick: options.onClick || false - }, - keyCodes: { - BACKSPACE: 8, - ESC: 27, - DELETE: 46 - }, - active: false, - initialized: false, - elements: {} - }; + var pub = {}, + self = { + opts: { + namespace: options.namespace || 'DomOutline', + borderWidth: options.borderWidth || 2, + onClick: options.onClick || false, + border: options.border || false, + realtime: options.realtime || false, + label: options.label || false + }, + keyCodes: { + BACKSPACE: 8, + ESC: 27, + DELETE: 46 + }, + active: false, + initialized: false, + elements: {} + }; function writeStylesheet(css) { var element = document.createElement('style'); @@ -44,12 +54,15 @@ var DomOutline = function (options) { } function initStylesheet() { + var css = ''; + if (self.initialized !== true) { - var css = '' + + css += '.' + self.opts.namespace + ' {' + - ' background: #09c;' + + ' background: rgba(0, 153, 204, 0.5);' + ' position: absolute;' + ' z-index: 1000000;' + + ' pointer-events: none;' + '}' + '.' + self.opts.namespace + '_label {' + ' background: #09c;' + @@ -60,6 +73,13 @@ var DomOutline = function (options) { ' position: absolute;' + ' text-shadow: 0 1px 1px rgba(0, 0, 0, 0.25);' + ' z-index: 1000001;' + + ' pointer-events: none;' + + '}' + + '.' + self.opts.namespace + '_box {' + + ' background: rgba(0, 153, 204, 0.5);' + + ' position: absolute;' + + ' z-index: 1000000;' + + ' pointer-events: none;' + '}'; writeStylesheet(css); @@ -68,15 +88,17 @@ var DomOutline = function (options) { } function createOutlineElements() { - self.elements.label = jQuery('
').addClass(self.opts.namespace + '_label').appendTo('body'); - self.elements.top = jQuery('
').addClass(self.opts.namespace).appendTo('body'); - self.elements.bottom = jQuery('
').addClass(self.opts.namespace).appendTo('body'); - self.elements.left = jQuery('
').addClass(self.opts.namespace).appendTo('body'); - self.elements.right = jQuery('
').addClass(self.opts.namespace).appendTo('body'); + self.elements.label = jQuery('
').addClass(self.opts.namespace + '_label').appendTo('body'); + self.elements.top = jQuery('
').addClass(self.opts.namespace).appendTo('body'); + self.elements.bottom = jQuery('
').addClass(self.opts.namespace).appendTo('body'); + self.elements.left = jQuery('
').addClass(self.opts.namespace).appendTo('body'); + self.elements.right = jQuery('
').addClass(self.opts.namespace).appendTo('body'); + + self.elements.box = jQuery('
').addClass(self.opts.namespace + '_box').appendTo('body'); } function removeOutlineElements() { - jQuery.each(self.elements, function(name, element) { + jQuery.each(self.elements, function (name, element) { element.remove(); }); } @@ -99,40 +121,57 @@ var DomOutline = function (options) { return self.elements.window.scrollTop(); } - function updateOutlinePosition(e) { - if (e.target.className.indexOf(self.opts.namespace) !== -1) { - return; + function stopOnEscape(e) { + if (e.keyCode === self.keyCodes.ESC || e.keyCode === self.keyCodes.BACKSPACE || e.keyCode === self.keyCodes.DELETE) { + pub.stop(); } - pub.element = e.target; - var b = self.opts.borderWidth; - var scroll_top = getScrollTop(); - var pos = pub.element.getBoundingClientRect(); - var top = pos.top + scroll_top; + return false; + } - var label_text = compileLabelText(pub.element, pos.width, pos.height); - var label_top = Math.max(0, top - 20 - b, scroll_top); - var label_left = Math.max(0, pos.left - b); + function draw(e) { + if (e.target.className.indexOf(self.opts.namespace) !== -1) { + return; + } - self.elements.label.css({ top: label_top, left: label_left }).text(label_text); - self.elements.top.css({ top: Math.max(0, top - b), left: pos.left - b, width: pos.width + b, height: b }); - self.elements.bottom.css({ top: top + pos.height, left: pos.left - b, width: pos.width + b, height: b }); - self.elements.left.css({ top: top - b, left: Math.max(0, pos.left - b), width: b, height: pos.height + b }); - self.elements.right.css({ top: top - b, left: pos.left + pos.width, width: b, height: pos.height + (b * 2) }); - } + pub.element = e.target; - function stopOnEscape(e) { - if (e.keyCode === self.keyCodes.ESC || e.keyCode === self.keyCodes.BACKSPACE || e.keyCode === self.keyCodes.DELETE) { - pub.stop(); + var b = self.opts.borderWidth, + scroll_top = getScrollTop(), + pos = pub.element.getBoundingClientRect(), + top = pos.top + scroll_top, + label_text = '', + label_top = 0, + label_left = 0; + + if (self.opts.label) { + label_text = compileLabelText(pub.element, pos.width, pos.height); + label_top = Math.max(0, top - 20 - b, scroll_top); + label_left = Math.max(0, pos.left - b); + self.elements.label.css({ top: label_top, left: label_left }).text(label_text); } - return false; + if (self.opts.border) { + self.elements.top.css({ top: Math.max(0, top - b), left: pos.left - b, width: pos.width + b, height: b }); + self.elements.bottom.css({ top: top + pos.height, left: pos.left - b, width: pos.width + b, height: b }); + self.elements.left.css({ top: top - b, left: Math.max(0, pos.left - b), width: b, height: pos.height + b }); + self.elements.right.css({ top: top - b, left: pos.left + pos.width, width: b, height: pos.height + (b * 2) }); + } else { + self.elements.box.css({ + top: pos.top, + left: pos.left, + width: pos.width, + height: pos.height + }); + } } function clickHandler(e) { - pub.stop(); - self.opts.onClick(pub.element); + if (!self.opts.realtime) { + draw(e); + } + self.opts.onClick(pub.element); return false; } @@ -141,13 +180,17 @@ var DomOutline = function (options) { if (self.active !== true) { self.active = true; createOutlineElements(); - jQuery('body').bind('mousemove.' + self.opts.namespace, updateOutlinePosition); + jQuery('body').bind('keyup.' + self.opts.namespace, stopOnEscape); if (self.opts.onClick) { setTimeout(function () { jQuery('body').bind('click.' + self.opts.namespace, clickHandler); }, 50); } + + if (self.opts.realtime) { + jQuery('body').bind('mousemove.' + self.opts.namespace, draw); + } } }; @@ -160,4 +203,4 @@ var DomOutline = function (options) { }; return pub; -}; +}; diff --git a/jquery.dom-outline-1.0.js~ b/jquery.dom-outline-1.0.js~ new file mode 100644 index 0000000..86970c4 --- /dev/null +++ b/jquery.dom-outline-1.0.js~ @@ -0,0 +1,206 @@ +/*global document:false*/ +/*global window:false*/ +/*global jQuery:false*/ +/*global setTimeout:false*/ + +/** + * Firebug/Web Inspector Outline Implementation using jQuery + * Tested to work in Chrome, FF, Safari. Buggy in IE ;( + * Andrew Childs + * + * Example Setup: + * var myClickHandler = function (element) { console.log('Clicked element:', element); } + * var myDomOutline = DomOutline({ onClick: myClickHandler }); + * + * Public API: + * myDomOutline.start(); + * myDomOutline.stop(); + */ +var DomOutline = function (options) { + 'use strict'; + + options = options || {}; + + var pub = {}, + self = { + opts: { + namespace: options.namespace || 'DomOutline', + borderWidth: options.borderWidth || 2, + onClick: options.onClick || false, + border: options.border || false, + realtime: options.realtime || false, + label: options.label || false + }, + keyCodes: { + BACKSPACE: 8, + ESC: 27, + DELETE: 46 + }, + active: false, + initialized: false, + elements: {} + }; + + function writeStylesheet(css) { + var element = document.createElement('style'); + element.type = 'text/css'; + document.getElementsByTagName('head')[0].appendChild(element); + + if (element.styleSheet) { + element.styleSheet.cssText = css; // IE + } else { + element.innerHTML = css; // Non-IE + } + } + + function initStylesheet() { + var css = ''; + + if (self.initialized !== true) { + css += + '.' + self.opts.namespace + ' {' + + ' background: rgba(0, 153, 204, 0.5);' + + ' position: absolute;' + + ' z-index: 1000000;' + + ' pointer-events: none;' + + '}' + + '.' + self.opts.namespace + '_label {' + + ' background: #09c;' + + ' border-radius: 2px;' + + ' color: #fff;' + + ' font: bold 12px/12px Helvetica, sans-serif;' + + ' padding: 4px 6px;' + + ' position: absolute;' + + ' text-shadow: 0 1px 1px rgba(0, 0, 0, 0.25);' + + ' z-index: 1000001;' + + ' pointer-events: none;' + + '}' + + '.' + self.opts.namespace + '_box {' + + ' background: rgba(0, 153, 204, 0.5);' + + ' position: absolute;' + + ' z-index: 1000000;' + + ' pointer-events: none;' + + '}'; + + writeStylesheet(css); + self.initialized = true; + } + } + + function createOutlineElements() { + self.elements.label = jQuery('
').addClass(self.opts.namespace + '_label').appendTo('body'); + self.elements.top = jQuery('
').addClass(self.opts.namespace).appendTo('body'); + self.elements.bottom = jQuery('
').addClass(self.opts.namespace).appendTo('body'); + self.elements.left = jQuery('
').addClass(self.opts.namespace).appendTo('body'); + self.elements.right = jQuery('
').addClass(self.opts.namespace).appendTo('body'); + + self.elements.box = jQuery('
').addClass(self.opts.namespace + '_box').appendTo('body'); + } + + function removeOutlineElements() { + jQuery.each(self.elements, function (name, element) { + element.remove(); + }); + } + + function compileLabelText(element, width, height) { + var label = element.tagName.toLowerCase(); + if (element.id) { + label += '#' + element.id; + } + if (element.className) { + label += ('.' + jQuery.trim(element.className).replace(/ /g, '.')).replace(/\.\.+/g, '.'); + } + return label + ' (' + Math.round(width) + 'x' + Math.round(height) + ')'; + } + + function getScrollTop() { + if (!self.elements.window) { + self.elements.window = jQuery(window); + } + return self.elements.window.scrollTop(); + } + + function stopOnEscape(e) { + if (e.keyCode === self.keyCodes.ESC || e.keyCode === self.keyCodes.BACKSPACE || e.keyCode === self.keyCodes.DELETE) { + pub.stop(); + } + + return false; + } + + function draw(e) { + if (e.target.className.indexOf(self.opts.namespace) !== -1) { + return; + } + + pub.element = e.target; + + var b = self.opts.borderWidth, + scroll_top = getScrollTop(), + pos = pub.element.getBoundingClientRect(), + top = pos.top + scroll_top, + label_text = '', + label_top = 0, + label_left = 0; + + if (self.opts.label) { + label_text = compileLabelText(pub.element, pos.width, pos.height); + label_top = Math.max(0, top - 20 - b, scroll_top); + label_left = Math.max(0, pos.left - b); + self.elements.label.css({ top: label_top, left: label_left }).text(label_text); + } + + if (self.opts.border) { + self.elements.top.css({ top: Math.max(0, top - b), left: pos.left - b, width: pos.width + b, height: b }); + self.elements.bottom.css({ top: top + pos.height, left: pos.left - b, width: pos.width + b, height: b }); + self.elements.left.css({ top: top - b, left: Math.max(0, pos.left - b), width: b, height: pos.height + b }); + self.elements.right.css({ top: top - b, left: pos.left + pos.width, width: b, height: pos.height + (b * 2) }); + } else { + self.elements.box.css({ + top: pos.top, + left: pos.left, + width: pos.width, + height: pos.height + }); + } + } + + function clickHandler(e) { + if (!self.opts.realtime) { + draw(e); + } + + self.opts.onClick(pub.element); + return false; + } + + pub.start = function () { + initStylesheet(); + if (self.active !== true) { + self.active = true; + createOutlineElements(); + + jQuery('body').bind('keyup.' + self.opts.namespace, stopOnEscape); + if (self.opts.onClick) { + setTimeout(function () { + jQuery('body').bind('click.' + self.opts.namespace, clickHandler); + }, 50); + } + + if (self.opts.realtime) { + jQuery('body').bind('mousemove.' + self.opts.namespace, draw); + } + } + }; + + pub.stop = function () { + self.active = false; + removeOutlineElements(); + jQuery('body').unbind('mousemove.' + self.opts.namespace) + .unbind('keyup.' + self.opts.namespace) + .unbind('click.' + self.opts.namespace); + }; + + return pub; +};