I've not done JavaScript in quite a while, but I wanted to make it so that I can circle (in many different ways) around the bounding box of a tagged item. Is this a good way of doing what I'm wanting? Is there a better way? I.e. can this be done with only CSS? http://jsfiddle.net/adrianh/4SVHH/27/ // Creating a tag with the class 'circled' will have that element circled // using a number of different ways by setting data-style attribute to: // 'default' - an open ellipse (default) // 'ellipse' - a closed ellipse // 'circle' - a closed circle circumscribed around the bounding box. // 'rect' - a closed rectangle // 'vbracket' - a square bracket like [ ] // 'hbracket' - a square bracket that is rotated 90 degrees // // Other attributes are: // data-width - width of lines used to circle element. (default='2') // data-style - can be: 'solid', 'dotted' or 'dashed'. (default='solid') // data-color - any html color. (default='red') // // Note: The circle is around the bounding box of the tagged object, // so if the object wraps, it will span the paragraph width. // // Note: Circle will be redrawn if window is resized. (function() { // draws an ellipse function drawEllipse(context, centerX, centerY, width, height) { var ratio = height/width; context.save(); context.scale(1, ratio); context.beginPath(); context.arc(centerX, centerY/ratio, width/2, 0, 2 * Math.PI); context.restore(); }; // returns the appropriate array for the style requested function getLineDash(style) { if (style == "solid") return []; if (style == 'dotted') return [3]; if (style == 'dashed') return [6, 3]; return []; }; // points at object that contains all the circle canvases var circles; // Return a canvas given an indexed number. If the same // number is used multiple times, then that canvas is reused. // Canvas is positioned in the (left, top) coordinate in the // document with the specified width and height. function getCanvas(i, left, top, width, height) { var canvas; if (typeof circles.childNodes[i] != 'undefined') { canvas = circles.childNodes[i]; canvas.width=width; canvas.height=height; canvas.style.left=left+"px"; canvas.style.top=top+"px"; } else { canvas = "<canvas "+ "width='"+width+"' "+ "height='"+height+"' "+ "style='"+ "position:absolute;"+ "z-index:0;"+ "left:"+left+"px;"+ "top:"+top+"px;"+ "pointer-events:none;"+ //"border:1px solid;"+ "' />"; circles.insertAdjacentHTML('beforeend', canvas); } return circles.childNodes[i]; }; // draw a circle circumscribed around the rect function circleRect(i, rect, lineWidth, lineColor, lineStyle) { var diameter = Math.sqrt(rect.width*rect.width+rect.height*rect.height); var cx = (rect.right + rect.left)/2; var cy = (rect.top + rect.bottom)/2; var left = Math.floor(cx - diameter/2); var top = Math.floor(cy - diameter/2); diameter = Math.floor(diameter); var canvas = getCanvas(i, left-1, top-1, diameter+2, diameter+2); var ctx=canvas.getContext("2d"); ctx.beginPath(); ctx.arc(diameter/2+1,diameter/2+1,diameter/2,0,2*Math.PI); ctx.lineWidth = lineWidth; ctx.strokeStyle = lineColor; ctx.setLineDash = getLineDash(lineStyle); ctx.stroke(); }; // draw an open ellipse around the rect. function defaultRect(i, rect, lineWidth, lineColor, lineStyle) { var quarterWidth = rect.width/4; var quarterHeight = rect.height/4; var eighthWidth = quarterWidth/2; var eighthHeight = quarterHeight/2; var padx = quarterWidth; var pady = quarterHeight*1.5; var width = rect.width; var height = rect.height; var canvas = getCanvas(i, rect.left-padx, rect.top-pady, width+padx*2, height+pady*2); //canvas.style.border="1px solid yellow"; var ctx=canvas.getContext("2d"); ctx.beginPath(); ctx.moveTo( padx+width/2, pady+eighthHeight); ctx.bezierCurveTo(padx-quarterWidth, pady+quarterHeight/3, padx, pady+height+eighthHeight, padx+width/2-eighthWidth, pady+height+eighthHeight); ctx.bezierCurveTo(padx+width+quarterWidth, pady+height+quarterHeight, padx+width+eighthWidth, pady-eighthHeight*3, padx+width/2-eighthWidth, pady-quarterHeight); ctx.lineWidth = lineWidth; ctx.strokeStyle = lineColor; ctx.setLineDash(getLineDash(lineStyle)); ctx.stroke(); // ctx.strokeStyle = "blue"; // ctx.lineWidth = 1; // ctx.strokeRect(padx, pady, width, height); }; // draw an ellipse around the rect function ellipseRect(i, rect, lineWidth, lineColor, lineStyle) { var padx = 200; var pady = 200; var canvas = getCanvas(i, rect.left-padx, rect.top-pady, rect.width+padx*2, rect.height+pady*2); var ctx=canvas.getContext("2d"); //canvas.style.border = "1px blue solid" drawEllipse(ctx, rect.width/2+padx, rect.height/2+pady, rect.width, rect.height); ctx.lineWidth = lineWidth; ctx.strokeStyle = lineColor; ctx.setLineDash(getLineDash(lineStyle)); ctx.stroke(); }; // Draw a rectangle around the rect. function rectRect(i, rect, lineWidth, lineColor, lineStyle) { var half = Math.ceil(lineWidth/2); var padx = lineWidth; var pady = lineWidth; var canvas = getCanvas(i, rect.left-padx, rect.top-pady, rect.width+padx*2, rect.height+pady*2); //canvas.style.border = "1px blue solid"; var ctx=canvas.getContext("2d"); ctx.lineWidth = lineWidth; ctx.strokeStyle = lineColor; ctx.setLineDash(getLineDash(lineStyle)); ctx.strokeRect(padx-half, pady-half, rect.width+lineWidth/2, rect.height+lineWidth/2); }; // Draw a rectangle around the rect. function vbracketRect(i, rect, lineWidth, lineColor, lineStyle) { var half = Math.ceil(lineWidth/2); var padx = lineWidth*2; var pady = lineWidth*2; var canvas = getCanvas(i, rect.left-padx, rect.top-pady, rect.width+padx*2, rect.height+pady*2); //canvas.style.border = "1px blue solid"; var ctx=canvas.getContext("2d"); ctx.lineWidth = lineWidth; ctx.strokeStyle = lineColor; ctx.setLineDash(getLineDash(lineStyle)); ctx.beginPath(); ctx.moveTo(padx-half+half*3 , pady-half); ctx.lineTo(padx-half , pady-half); ctx.lineTo(padx-half , pady+half+rect.height); ctx.lineTo(padx-half+half*3 , pady+half+rect.height); ctx.moveTo(padx+half+rect.width-half*3 , pady-half); ctx.lineTo(padx+half+rect.width , pady-half); ctx.lineTo(padx+half+rect.width , pady+half+rect.height); ctx.lineTo(padx+half+rect.width-half*3 , pady+half+rect.height); ctx.stroke(); }; function hbracketRect(i, rect, lineWidth, lineColor, lineStyle) { var half = Math.ceil(lineWidth/2); var padx = lineWidth*2; var pady = lineWidth*2; var canvas = getCanvas(i, rect.left-padx, rect.top-pady, rect.width+padx*2, rect.height+pady*2); //canvas.style.border = "1px blue solid"; var ctx=canvas.getContext("2d"); ctx.lineWidth = lineWidth; ctx.strokeStyle = lineColor; ctx.setLineDash(getLineDash(lineStyle)); ctx.beginPath(); ctx.moveTo(padx-half , pady-half+half*3); ctx.lineTo(padx-half , pady-half); ctx.lineTo(padx+half+rect.width , pady-half); ctx.lineTo(padx+half+rect.width , pady-half+half*3); ctx.moveTo(padx-half , pady+half+rect.height-half*3); ctx.lineTo(padx-half , pady+half+rect.height); ctx.lineTo(padx+half+rect.width , pady+half+rect.height); ctx.lineTo(padx+half+rect.width , pady+half+rect.height-half*3); ctx.stroke(); }; // if x is null return _default, otherwise return x. function val(x, _default) { return (x == null) ? _default : x; }; // This draws the circles around all 'circled' class objects. function drawCircles() { $(".circled").each(function(i, obj) { var rect = obj.getBoundingClientRect(); var type = val(obj.getAttribute("data-type"), "default"); var width = val(obj.getAttribute("data-width"), 2); var color = val(obj.getAttribute("data-color"), "red"); var style = val(obj.getAttribute("data-style"), "solid"); if (type == 'default') { defaultRect(i, rect, width, color, style); } else if (type == 'rect') { rectRect(i, rect, width, color, style); } else if (type == 'ellipse') { ellipseRect(i, rect, width, color, style); } else if (type == 'vbracket') { vbracketRect(i, rect, width, color, style); } else if (type == 'hbracket') { hbracketRect(i, rect, width, color, style); } else if (type == 'circle') { circleRect(i, rect, width, color, style); } }); }; // Place a div container to place all of the canvases in. document.body.insertAdjacentHTML('afterbegin', '<div id="circles"></div>'); circles = document.getElementById("circles"); // Draw the circles upon loading the document and // if the window extents have changed. window.onload=drawCircles; window.onresize=drawCircles; })();