Hover and Click Trigger for Circular Elements with jQuery

Today we want to share one possible solution to the circle hovering problem. We’ll create a plugin that will take care of the ‘mouseenter’, ‘mouseleave’ and ‘click’ events to be triggered only on the circular shape of the element and not its bounding box.

HoverClickTriggerCircles

Applying a :hover pseudo-class to an element is widely known as the classic “hovering” over an element on a web page. A problem that arose with the introduction of the border-radius property is the non-realistic triggering of the hover event when entering the bounding box of the element and not just the actual visible area. This becomes extreme when we create a circle by setting the border-radius of a square to 50% (half of its outer width and height).

Today we want to share one possible solution to the circle hovering problem. We’ll create a plugin that will take care of the ‘mouseenter’, ‘mouseleave’ and ‘click’ events to be triggered only on the circular shape of the element and not its bounding box.

The image used in the demo is by Andrey & Lili. They are licensed under the Attribution-NonCommercial 3.0 Unported (CC BY-NC 3.0) License.

How it works

In our example, we’ll be creating a circle with some kind of hover effect. The structure will simply be:

<a href="#" id="circle" class="ec-circle">
	<h3>Hovered</h3>
</a>

And the style will be the following:

.ec-circle{
	width: 420px;
	height: 420px;
	-webkit-border-radius: 210px;
	-moz-border-radius: 210px;
	border-radius: 50%;
	text-align: center;
	overflow: hidden;
	font-family:'Kelly Slab', Georgia, serif;
	background: #dda994 url(../images/1.jpg) no-repeat center center;
	box-shadow: 
		inset 0 0 1px 230px rgba(0,0,0,0.6),
		inset 0 0 0 7px #d5ad94;
	transition: box-shadow 400ms ease-in-out;
	display: block;
	outline: none;
}

Now, we will define a class for the hover effect but not a dynamic pseudo-class :hover. The idea is to apply this class then with jQuery when we enter the circular area of our element:

.ec-circle-hover{
    box-shadow: 
		inset 0 0 0 0 rgba(0,0,0,0.6),
		inset 0 0 0 20px #c18167,
		0 0 10px rgba(0,0,0,0.3);
}

Only when we have JavaScript disabled, we’ll add the pseudo-class. This style can be found in the noscript.css:

.ec-circle:hover{
    box-shadow: 
		inset 0 0 0 0 rgba(0,0,0,0.6),
		inset 0 0 0 20px #c18167,
		0 0 10px rgba(0,0,0,0.3);
}

The JavaScript

We are going to create a simple plugin that basically “redefines” the three events mentioned earlier. We’ll make the events only applicable on the circular shape:

$.CircleEventManager 			= function( options, element ) {
	
	this.$el			= $( element );
	
	this._init( options );
	
};

$.CircleEventManager.defaults 	= {
	onMouseEnter	: function() { return false },
	onMouseLeave	: function() { return false },
	onClick			: function() { return false }
};

$.CircleEventManager.prototype 	= {
	_init 				: function( options ) {
		
		this.options 		= $.extend( true, {}, $.CircleEventManager.defaults, options );
		
		// set the default cursor on the element
		this.$el.css( 'cursor', 'default' );
		
		this._initEvents();
		
	},
	_initEvents			: function() {
		
		var _self	= this;
		
		this.$el.on({
			'mouseenter.circlemouse'	: function( event ) {
				
				var el	= $(event.target),
				
						  circleWidth	= el.outerWidth( true ),
						  circleHeight	= el.outerHeight( true ),
						  circleLeft	= el.offset().left,
						  circleTop		= el.offset().top,
						  circlePos		= {
							  x		: circleLeft + circleWidth / 2,
							  y		: circleTop + circleHeight / 2,
							  radius: circleWidth / 2
						  };
				
				// save cursor type
				var cursor	= 'default';
				
				if( _self.$el.css('cursor') === 'pointer' || _self.$el.is('a') )
					cursor = 'pointer';
					
				el.data( 'cursor', cursor );
				
				el.on( 'mousemove.circlemouse', function( event ) {

					var distance	= Math.sqrt( Math.pow( event.pageX - circlePos.x, 2 ) + Math.pow( event.pageY - circlePos.y, 2 ) );
					
					if( !Modernizr.borderradius ) {
						
						// inside element / circle
						el.css( 'cursor', el.data('cursor') ).data( 'inside', true );
						_self.options.onMouseEnter( _self.$el );
					
					}
					else {
					
						if( distance <= circlePos.radius && !el.data('inside') ) {
							
							// inside element / circle
							el.css( 'cursor', el.data('cursor') ).data( 'inside', true );
							_self.options.onMouseEnter( _self.$el );
							
						}
						else if( distance > circlePos.radius && el.data('inside') ) {
							
							// inside element / outside circle
							el.css( 'cursor', 'default' ).data( 'inside', false );
							_self.options.onMouseLeave( _self.$el );
						
						}
					
					}
				
				});	
				
			},
			'mouseleave.circlemouse'	: function( event ) {
				
				var el 	= $(event.target);
	
				el.off('mousemove');
				
				if( el.data( 'inside' ) ) {
				
					el.data( 'inside', false );
					_self.options.onMouseLeave( _self.$el );
				
				}
				
			},
			'click.circlemouse'			: function( event ) {
				
				// allow the click only when inside the circle
				
				var el 	= $(event.target);
				
				if( !el.data( 'inside' ) )
					return false;
				else
					_self.options.onClick( _self.$el );
				
			}
		});
		
	},
	destroy				: function() {
	
		this.$el.unbind('.circlemouse').removeData('inside').removeData('cursor');

	}
};

When we enter with the mouse in the square bounding box of our circle, we bind the ‘mousemove’ event to the element and like that we can track if the distance of the mouse to the center of the element if longer than the radius. If it is, we know that we are not yet hovering the circular area of the element.

HoverTrigger
Once the distance of the mouse is shorter than the radius, we know that we entered the circle and we trigger our custom ‘mouseenter’ event.

We also only allow the click event when the mouse is inside of the circle.

In our example we will then apply our plugin to the regarding element. In our case, we are adding the hover class on ‘mouseenter’ and removing it on ‘mouseleave’.

$('#circle').circlemouse({
	onMouseEnter	: function( el ) {
	
		el.addClass('ec-circle-hover');
	
	},
	onMouseLeave	: function( el ) {
		
		el.removeClass('ec-circle-hover');
		
	},
	onClick			: function( el ) {
		
		alert('clicked');
		
	}
});

Remember that the “normal” pseudo hover class is also defined in the noscript.css which gets applied when JavaScript is disabled.

I hope you find this useful!

Tagged with:

Manoela Ilic

Manoela is the main tinkerer at Codrops. With a background in coding and passion for all things design, she creates web experiments and keeps frontend professionals informed about the latest trends.

Stay up to date with the latest web design and development news and relevant updates from Codrops.