DZone Snippets is a public source code repository. Easily build up your personal collection of code snippets, categorize them with tags / keywords, and share them with the world

Jon has posted 3 posts at DZone. View Full User Profile

DOM Mouse-Over Element Selection And Isolation Via XPath

09.03.2007
| 2115 views |
  • submit to reddit
        //DOM ISO.v.0.2.8.4.bookmarklet
//bookmarklet for isolating an element on a page.
//two sections:
//section 1:  Mouseover DOM, setup and handle mouse events and show information about element in div.  Click to select, Any key to cancel.
//section 2:  Element Isolation with help of XPath.  prompt user for (very) simple XPath expression  e.g., DIV[@id='post-body'].  then use XPath to select all element not(ancestor or descendant or self), then delete those elements.  also ignore self-or-descendants of head and title.
//currently only a single, simple XPath expression is supported, this code needs more work, but on the right track.

//tools:
//Ruderman's javascript development environment:  https://www.squarefree.com/bookmarklets/webdevel.html#jsenv
//Mielczarek's js to bookmarklet generator:  http://ted.mielczarek.org/code/mozilla/bookmarklet.html

(function() {
	//GLOBALS
		//globals for classMausWork
		var gSelectedElement;	//currently only one selection
		var gHoverElement;		//whatever element the mouse is over
		var gHovering=false;	//mouse is over something
		var gObjArrMW=[];	//global array of classMausWork objects.  for removing event listeners when done selecting.
		
		//extended	
		var infoDiv;		//currently just container for InfoDivHover, might add more here
		var infoDivHover;	//container for hoverText text node.
		var hoverText;		//show information about current element that the mouse is over
		//const EXPERIMENTAL_NEW_CODE=true;	//debugging. new features.
	
	
	//START
		SetupDOMSelection();	



		
	//(Section 1) Element Selection
	function SetupDOMSelection()
	{

		{
			//setup event listeners
			//var pathx="//div | //span | //table | //td | //tr | //ul | //ol | //li | //p";
			var pathx="//div | //span | //table | //th | //td | //tr | //ul | //ol | //li | //p | //iframe";
			var selection=$XPathSelect(pathx);
			for(var element, i=0;element=selection(i);i++)
			{			
				if(element.tagName.match(/^(div|span|table|td|tr|ul|ol|li|p)$/i))	//redundant check.
				{
					var m = new classMausWork(element);
					gObjArrMW.push(m);
					attachMouseEventListeners(m);
				}
			}
			document.body.addEventListener('mousedown',MiscEvent,false);
			document.body.addEventListener('mouseover',MiscEvent,false);
			document.body.addEventListener('mouseout',MiscEvent,false);
			document.addEventListener('keypress',MiscEvent,false);
		}
		{
			//setup informational div to show which element the mouse is over.
			infoDiv=document.createElement('div');
			var s=infoDiv.style;
			s.position='fixed';
			s.top='0';
			s.right='0';
			
			s.display='block';
			s.width='auto';
			s.padding='0px';

			document.body.appendChild(infoDiv);
			infoDivHover=document.createElement('div');

			s=infoDivHover.style;
			s.fontWeight='bold';			
			s.padding='3px';
			s.Opacity='0.8';
			s.borderWidth='thin';
			s.borderStyle='solid';
			s.borderColor='white';
			s.backgroundColor='black';
			s.color='white';
			
			infoDiv.appendChild(infoDivHover);			
			hoverText=document.createTextNode('selecting');
			infoDivHover.appendChild(hoverText);
		}
	}
	
	function CleanupDOMSelection()
	{
		for(var m; m=gObjArrMW.pop(); )
		{
			detachMouseEventListeners(m);
		}
		ElementRemove(infoDiv);
		document.body.removeEventListener('mousedown',MiscEvent,false);
		document.body.removeEventListener('mouseover',MiscEvent,false);
		document.body.removeEventListener('mouseout',MiscEvent,false);		
		document.removeEventListener('keypress',MiscEvent,false);
	}	

	function attachMouseEventListeners(c)
	{
		//c is object of class classMausWork
		c.element.addEventListener("mouseover",c.mouse_over,false);				
		c.element.addEventListener("mouseout",c.mouse_out,false);	
		c.element.addEventListener("mousedown",c.mouse_click,false);		
	}

	function detachMouseEventListeners(c)
	{
		//c is object of class classMausWork
		c.resetElementStyle();
		c.element.removeEventListener("mouseover",c.mouse_over,false);				
		c.element.removeEventListener("mouseout",c.mouse_out,false);	
		c.element.removeEventListener("mousedown",c.mouse_click,false);		
	}

	//mouse event  handling class for element, el.
	function classMausWork(element)
	{	
		//store information about the element this object is assigned to handle. element,  original style, etc.	
		this.element=element;
		var defaultbgc=element.style.backgroundColor;
		var defaultStyle=element.getAttribute('style');	
		
		this.mouse_over=function(ev)
		{	
			if(gHovering)return;			
			if(ev.target!=element)return;
			var e=element;		//var e=ev.target;
			var s=e.style;
			s.backgroundColor='yellow';
			s.borderWidth='thin';
			s.borderColor='lime';
			s.borderStyle='solid';					
			InfoMSG(ElementInfo(e),'yellow','blue','yellow');				
			gHoverElement=e;		//gHoverElement=ev.target;	//gHoverElement=e;	
			gHovering=true;
			ev.stopPropagation();		
		};	
		this.mouse_out=function(ev)
		{
			if(ev.target!=gHoverElement)return;
			var e=element;		//var e=ev.target;
			e.setAttribute('style',defaultStyle);	// ev.target.setAttribute('style',defaultStyle);			
			InfoMSG('-','white','black','white');	
			gHoverElement=null;
			gHovering=false;
			//ev.stopPropagation();
		};
		this.mouse_click=function(ev)
		{

			if(ev.target!=gHoverElement)return;
			var e=element;		//var e=ev.target;
			e.setAttribute('style',defaultStyle);  //ev.target.setAttribute('style',defaultStyle);			
			gSelectedElement=e;		//gSelectedElement=ev.target;		//=ev.target;			
			ev.stopPropagation();			
			CleanupDOMSelection();			
			gHoverElement=null;
			gHovering=false;		
			ElementSelected(gSelectedElement);	//finished selecting, cleanup then move to next part, element isolation.
		};
		this.resetElementStyle=function()
		{
			if(gHovering && gHoverElement && gHoverElement==this.element)
			{
				this.element.setAttribute('style',defaultStyle);
			}
		};		
	}

	function MiscEvent(ev)		//keypress, and mouseover/mouseout/mousedown event on body.  cancel selecting.
	{
		if(ev.type=='mouseout')
		{
			InfoMSG('-','white','black','white');
		}
		else if(ev.type=='mouseover')
		{
			InfoMSG('cancel','yellow','red','yellow');
		}
		else //keypress on document or mousedown on body, cancel ops.
		{
			CleanupDOMSelection();
		}
	}
	
	function InfoMSG(text,color,bgcolor,border)
	{
		
		var s=infoDivHover.style;
		//var s=infoDiv.style;		
		if(color)s.color=color;
		if(bgcolor)s.backgroundColor=bgcolor;
		if(border)s.borderColor=border;
		if(text)hoverText.data=text;
	}
	


	
	
	//(Section 2) Element Isolation
	function ElementSelected(element)	//finished selecting element.  setup string to prompt user.
	{
		PromptUserXpath(ElementInfo(element));
	}
	
	
	function PromptUserXpath(defaultpath)		//prompt user, isolate element.
	{
		var userpath = prompt("XPath of elements to isolate : ", defaultpath);
		if(userpath && userpath.length>0)
		{

			var pathx=TransformToNonIsolatePath(userpath);
			try
			{
				var element;
				var elements=$XPathSelect(pathx);	
				for(var i=0;element=elements(i);i++)
				{			

					if(!element.nodeName.match(/^(head|title)$/i))
					{
						ElementRemove(element);
					}
				}
			}
			
			catch(err)
			{
				alert("wtf: "+err);
			}
			
		}
	}


		//(under construction) work on this function
		//currently only simple expressions are converted.
		function TransformToNonIsolatePath(u)
		{
			var i="";
			i+="//*[count(./descendant-or-self::";
			i+=u;
			i+=")=0][count(./ancestor-or-self::";
			i+=u;
			i+=")=0]";			
			i+="[count(./ancestor-or-self::head)=0][count(./ancestor-or-self::title)=0]";			
			return i;								
		}
		
	
	//support
	function $XPathSelect(p, context) 
	{
	  if (!context) context = document;
	  var i, arr = [], xpr = document.evaluate(p, context, null, XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE, null);
	  return function(x) { return xpr.snapshotItem(x); };	//closure.  wooot!  returns function-type array of elements (usually elements, or something else depending on the xpath expression).
	}
	
	function ElementRemove(e)
	{
		e.parentNode.removeChild(e);
	}
	
	function ElementInfo(element)
	{
		var txt='';
		txt=element.tagName;
		txt=attrib(txt,element,'id');
		txt=attrib(txt,element,'class');	
		return txt;
		
		function attrib(t,e,a)
		{			
			if(e.hasAttribute(a))
			{
				t+="[@"+a+"='"+e.getAttribute(a)+"']";
			}
			return t;
		}
		
	}
	
})();