Display full sized image on <img> hover? - javascript

I have an <img> in an MVC 4 Razor Display Template, and I'd like to display a tooltip containing the full sized image when the user hovers over the image.
HTML:
<img height="50" width="50" src="#Model.ImageString" />
#Model.ImageString contains an image data string, which looks like this:
"data:image/*;base64," + Convert.ToBase64String(file.Data)
If you couldn't guess, file.Data is a byte[].
How can I display a full-sized tooltip upon hovering the <img>?

Here is a very quick example: http://jsfiddle.net/bGn96/
This is along the lines of what Shan Robertson is suggesting.
var $tooltip = $('#fullsize');
$('img').on('mouseenter', function() {
var img = this,
$img = $(img),
offset = $img.offset();
$tooltip
.css({
'top': offset.top,
'left': offset.left
})
.append($img.clone())
.removeClass('hidden');
});
$tooltip.on('mouseleave', function() {
$tooltip.empty().addClass('hidden');
});
A library that provides the desired functionality can be found here: http://cssglobe.com/lab/tooltip/02/

var Controls = {
init: function () {
var imgLink = document.getElementById('thumb');
imgLink.addEventListener('mouseover', Controls.mouseOverListener, false );
imgLink.addEventListener('mouseout', Controls.mouseOutListener, false );
},
mouseOverListener: function ( event ) {
Controls.displayTooltip ( this );
},
mouseOutListener: function ( event ) {
Controls.hideTooltip ( this );
},
displayTooltip: function ( imgLink ) {
var tooltip = document.createElement ( "div" );
var fullImg = document.createElement ( "img" );
fullImg.src = imgLink.src;
tooltip.appendChild ( fullImg );
tooltip.className = "imgTooltip";
tooltip.style.top = "60px";
imgLink._tooltip = tooltip;
Controls._tooltip = tooltip;
imgLink.parentNode.appendChild ( tooltip );
imgLink.addEventListener ( "mousemove", Controls.followMouse, false);
},
hideTooltip : function ( imgLink ) {
imgLink.parentNode.removeChild ( imgLink._tooltip );
imgLink._tooltip = null;
Controls._tooltip = null;
},
mouseX: function ( event ) {
if ( !event ) event = window.event;
if ( event.pageX ) return event.pageX;
else if ( event.clientX )
return event.clientX + (document.documentElement.scrollLeft ?
document.documentElement.scrollLeft :
document.body.scrollLeft);
else return 0;
},
mouseY: function ( event ) {
if (!event) event = window.event;
if (event.pageY) return event.pageY;
else if (event.clientY)
return event.clientY + (document.documentElement.scrollTop ?
document.documentElement.scrollTop :
document.body.scrollTop);
else return 0;
},
followMouse: function ( event ) {
var tooltip = Controls._tooltip.style;
var offX = 15, offY = 15;
tooltip.left = (parseInt(Controls.mouseX(event))+offX) + 'px';
tooltip.top = (parseInt(Controls.mouseY(event))+offY) + 'px';
}
};
Controls.init();
EDIT:
Fiddle: http://jsfiddle.net/enzoferber/SyJsF/2/
Now the tooltip will follow the mouse.
mouseX() and mouseY() will return the current [x,y] mouse coords. And the follow listener is made with the 'mousemove' event that is attached after the tooltip is created.
Oh, and yeah, I changed the image. Now everyone can be happy....

Assuming you are using Javascript to do this:
Have a tooltip container ready in the dom
on hover, grab the file href and make a new image tag inside of the tooltip container.
Just make sure to not specify the image dimensions in the tag, or if you do, display the fullsize dimensions not 50x50.

Related

Auto Scaling Image Annotation

Scalize is a jQuery plugin used for adding custom markers (hotspots) with custom popovers and animations to containers or images.
But now When I click one by one on pointer it show all one by one But I am trying to show only one so when click another pointer will close the other which already opened.
Here is my EXAMPLE
(function(jQuery) {
"use strict";
//----------------------------------------//
// Variable
//----------------------------------------//
var variable = {
width : 0,
height : 0,
selector : '.item-point',
styleSelector : 'circle',
animationSelector : 'pulse2',
animationPopoverIn : 'flipInY',
animationPopoverOut : 'flipOutY',
onInit : null,
getSelectorElement : null,
getValueRemove : null
}
//----------------------------------------//
// Scaling
//----------------------------------------//
var scaling = {
settings : null,
//----------------------------------------//
// Initialize
//----------------------------------------//
init: function(el, options){
this.settings = jQuery.extend(variable, options);
this.event(el);
scaling.layout(el);
jQuery(window).on('load', function(){
scaling.layout(el);
});
jQuery(el).find('.target').on('load', function(){
scaling.layout(el);
});
jQuery(window).on('resize', function(){
scaling.layout(el);
});
},
//----------------------------------------//
// Event
//----------------------------------------//
event : function(elem){
// Set Style Selector
if ( this.settings.styleSelector ) {
jQuery(this.settings.selector).addClass( this.settings.styleSelector );
}
// Set Animation
if ( this.settings.animationSelector ) {
if( this.settings.animationSelector == 'marker' ){
jQuery(this.settings.selector).addClass( this.settings.animationSelector );
jQuery(this.settings.selector).append('<div class="pin"></div>')
jQuery(this.settings.selector).append('<div class="pulse"></div>')
}else{
jQuery(this.settings.selector).addClass( this.settings.animationSelector );
}
}
// Event On Initialize
if ( jQuery.isFunction( this.settings.onInit ) ) {
this.settings.onInit();
}
// Content add class animated element
jQuery(elem).find('.content').addClass('animated');
// Wrapper selector
jQuery(this.settings.selector).wrapAll( "<div class='wrap-selector' />");
// Event Selector
jQuery(this.settings.selector).each(function(){
// Toggle
jQuery('.toggle', this).on('click', function(e){
e.preventDefault();
jQuery(this).closest(scaling.settings.selector).toggleClass('active');
// Selector Click
var content = jQuery(this).closest(scaling.settings.selector).data('popover'),
id = jQuery(content);
if(jQuery(this).closest(scaling.settings.selector).hasClass('active') && !jQuery(this).closest(scaling.settings.selector).hasClass('disabled')){
if ( jQuery.isFunction( scaling.settings.getSelectorElement ) ) {
scaling.settings.getSelectorElement(jQuery(this).closest(scaling.settings.selector));
}
id.fadeIn(500,function(){
if( getBrowserName() == "Safari" ){
setTimeout(function(){
id.removeClass('flipInY');
},125);
}
});
scaling.layout(elem);
id.removeClass(scaling.settings.animationPopoverOut);
id.addClass(scaling.settings.animationPopoverIn);
}else{
if(jQuery.isFunction( scaling.settings.getValueRemove )){
scaling.settings.getValueRemove(jQuery(this).closest(scaling.settings.selector));
}
id.removeClass(scaling.settings.animationPopoverIn);
id.addClass(scaling.settings.animationPopoverOut);
id.delay(500).fadeOut();
}
});
// Exit
var target = jQuery(this).data('popover'),
idTarget = jQuery(target);
idTarget.find('.exit').on('click', function(e){
e.preventDefault();
// selector.removeClass('active');
jQuery('[data-popover="'+ target +'"]').removeClass('active');
idTarget.removeClass(scaling.settings.animationPopoverIn);
idTarget.addClass(scaling.settings.animationPopoverOut);
idTarget.delay(500).fadeOut();
});
});
},
//----------------------------------------//
// Layout
//----------------------------------------//
layout : function(elem){
// Get Original Image
var image = new Image();
image.src = elem.find('.target').attr("src");
// Variable
var width = image.naturalWidth,
height = image.naturalHeight,
getWidthLess = jQuery(elem).width(),
setPersenWidth = getWidthLess/width * 100,
setHeight = height * setPersenWidth / 100;
// Set Heigh Element
jQuery(elem).css("height", setHeight);
// Resize Width
if( jQuery(window).width() < width ){
jQuery(elem).stop().css("width","100%");
}else{
jQuery(elem).stop().css("width",width);
}
// Set Position Selector
jQuery(this.settings.selector).each(function(){
if( jQuery(window).width() < width ){
var getTop = jQuery(this).data("top") * setPersenWidth / 100,
getLeft = jQuery(this).data("left") * setPersenWidth / 100;
}else{
var getTop = jQuery(this).data("top"),
getLeft = jQuery(this).data("left");
}
jQuery(this).css("top", getTop + "px");
jQuery(this).css("left", getLeft + "px");
// Target Position
var target = jQuery(this).data('popover'),
allSize = jQuery(target).find('.head').outerHeight() + jQuery(target).find('.body').outerHeight() + jQuery(target).find('.footer').outerHeight();
jQuery(target).css("left", getLeft + "px");
jQuery(target).css("height", allSize + "px");
if(jQuery(target).hasClass('bottom')){
var getHeight = jQuery(target).outerHeight(),
getTopBottom = getTop - getHeight;
jQuery(target).css("top", getTopBottom + "px");
}else if(jQuery(target).hasClass('center')){
var getHeight = jQuery(target).outerHeight() * 0.50,
getTopBottom = getTop - getHeight;
jQuery(target).css("top", getTopBottom + "px");
}else{
jQuery(target).css("top", getTop + "px");
}
jQuery('.toggle', this).css('width', jQuery(this).outerWidth());
jQuery('.toggle', this).css('height', jQuery(this).outerHeight());
// Toggle Size
if(jQuery(this).find('.pin')){
var widthThis = jQuery('.pin', this).outerWidth(),
heightThis = jQuery('.pin', this).outerHeight();
jQuery('.toggle', this).css('width', widthThis);
jQuery('.toggle', this).css('height', heightThis);
}
});
}
};
//----------------------------------------//
// Scalize Plugin
//----------------------------------------//
jQuery.fn.scalize = function(options){
return scaling.init(this, options);
};
}(jQuery));
function getBrowserName() {
var name = "Unknown";
if(navigator.userAgent.indexOf("MSIE")!=-1){
name = "MSIE";
}
else if(navigator.userAgent.indexOf("Firefox")!=-1){
name = "Firefox";
}
else if(navigator.userAgent.indexOf("Opera")!=-1){
name = "Opera";
}
else if(navigator.userAgent.indexOf("Chrome") != -1){
name = "Chrome";
}
else if(navigator.userAgent.indexOf("Safari")!=-1){
name = "Safari";
}
return name;
}
Add this to your initialisation:
getSelectorElement: function(el) {
$('.item-point.active').not($(el)[0]).find('.toggle').click();
}
This hooks into the getSelectorElement method in the Scalize plugin and triggers a click on any active (open) elements that don't match the most recently clicked item.
Add it like so:
$(document).ready(function(){
$('.scalize').scalize({
styleSelector: 'circle',
animationPopoverIn: 'fadeIn',
animationPopoverOut: 'fadeOut',
animationSelector: 'pulse2',
getSelectorElement: function(el) {
$('.item-point.active').not($(el)[0]).find('.toggle').click();
}
});
});
Note, because this is hooking into existing methods in the plugin it's a little safer (no unpredictable side effects, plus you get the intended transition out on the disappearing elements). Fiddle.
I've modified your jsFiddle to work.
TL;DR: Anytime an point is clicked, if there are other active siblings, loop over them and hide their popups.
It isn't a pretty way of doing it but it is working in the Fiddle.
$('.scalize').on('click', '.item-point', (function() {
$(this).siblings('.item-point.active').each(function() {
var popover = $(this).data('popover');
$(popover).removeClass('fadeIn').css({
'display': 'none'
});
$(this).removeClass('active');
});
}));

Make a slider responsive (jQuery)

I have a slider, I want to make them responsive the problem is the size of slider is into the jQuery file
sow how I can edit the code ?
code of jQuery
(function( window, $, undefined ) {
var $event = $.event, resizeTimeout;
$event.special.smartresize = {
setup: function() {
$(this).bind( "resize", $event.special.smartresize.handler );
},
teardown: function() {
$(this).unbind( "resize", $event.special.smartresize.handler );
},
handler: function( event, execAsap ) {
// Save the context
var context = this,
args = arguments;
// set correct event type
event.type = "smartresize";
if ( resizeTimeout ) { clearTimeout( resizeTimeout ); }
resizeTimeout = setTimeout(function() {
jQuery.event.handle.apply( context, args );
}, execAsap === "execAsap"? 0 : 100 );
}
};
$.fn.smartresize = function( fn ) {
return fn ? this.bind( "smartresize", fn ) : this.trigger( "smartresize", ["execAsap"] );
};
$.Slideshow = function( options, element ) {
this.$el = $( element );
/***** images ****/
// list of image items
this.$list = this.$el.find('ul.ei-slider-large');
// image items
this.$imgItems = this.$list.children('li');
// total number of items
this.itemsCount = this.$imgItems.length;
// images
this.$images = this.$imgItems.find('img:first');
/***** thumbs ****/
// thumbs wrapper
this.$sliderthumbs = this.$el.find('ul.ei-slider-thumbs').hide();
// slider elements
this.$sliderElems = this.$sliderthumbs.children('li');
// sliding div
this.$sliderElem = this.$sliderthumbs.children('li.ei-slider-element');
// thumbs
this.$thumbs = this.$sliderElems.not('.ei-slider-element');
// initialize slideshow
this._init( options );
};
$.Slideshow.defaults = {
// animation types:
// "sides" : new slides will slide in from left / right
// "center": new slides will appear in the center
animation : 'center', // sides || center
// if true the slider will automatically slide, and it will only stop if the user clicks on a thumb
autoplay : false,
// interval for the slideshow
slideshow_interval : 3000,
// speed for the sliding animation
speed : 800,
// easing for the sliding animation
easing : '',
// percentage of speed for the titles animation. Speed will be speed * titlesFactor
titlesFactor : 0.60,
// titles animation speed
titlespeed : 800,
// titles animation easing
titleeasing : '',
};
$.Slideshow.prototype = {
_init : function( options ) {
this.options = $.extend( true, {}, $.Slideshow.defaults, options );
// set the opacity of the title elements and the image items
this.$imgItems.css( 'opacity', 0 );
this.$imgItems.find('div.ei-title > *').css( 'opacity', 0 );
// index of current visible slider
this.current = 0;
var _self = this;
// preload images
// add loading status
this.$loading = $('<div class="ei-slider-loading">Loading</div>').prependTo( _self.$el );
$.when( this._preloadImages() ).done( function() {
// hide loading status
_self.$loading.hide();
// calculate size and position for each image
_self._setImagesSize();
// configure thumbs container
_self._initThumbs();
// show first
_self.$imgItems.eq( _self.current ).css({
'opacity' : 1,
'z-index' : 10
}).show().find('div.ei-title > *').css( 'opacity', 1 );
// if autoplay is true
if( _self.options.autoplay ) {
_self._startSlideshow();
}
// initialize the events
_self._initEvents();
});
},
_preloadImages : function() {
// preloads all the large images
var _self = this,
loaded = 0;
return $.Deferred(
function(dfd) {
_self.$images.each( function( i ) {
$('<img/>').load( function() {
if( ++loaded === _self.itemsCount ) {
dfd.resolve();
}
}).attr( 'src', $(this).attr('src') );
});
}
).promise();
},
_setImagesSize : function() {
// save ei-slider's width
this.elWidth = this.$el.width();
var _self = this;
this.$images.each( function( i ) {
var $img = $(this);
imgDim = _self._getImageDim( $img.attr('src') );
$img.css({
width : imgDim.width,
height : imgDim.height,
marginLeft : imgDim.left,
marginTop : imgDim.top
});
});
},
_getImageDim : function( src ) {
var $img = new Image();
$img.src = src;
var c_w = this.elWidth,
c_h = this.$el.height(),
r_w = c_h / c_w,
i_w = $img.width,
i_h = $img.height,
r_i = i_h / i_w,
new_w, new_h, new_left, new_top;
if( r_w > r_i ) {
new_h = c_h;
new_w = c_h / r_i;
}
else {
new_h = c_w * r_i;
new_w = c_w;
}
return {
width : new_w,
height : new_h,
left : ( c_w - new_w ) / 2,
top : ( c_h - new_h ) / 2
};
},
_initThumbs : function() {
// set the max-width of the slider elements to the one set in the plugin's options
// also, the width of each slider element will be 100% / total number of elements
this.$sliderElems.css({
'width' : 100 / this.itemsCount + '%'
});
// set the max-width of the slider and show it
this.$sliderthumbs.css( 'width', this.options.thumbMaxWidth * this.itemsCount + 'px' ).show();
},
_startSlideshow : function() {
var _self = this;
this.slideshow = setTimeout( function() {
var pos;
( _self.current === _self.itemsCount - 1 ) ? pos = 0 : pos = _self.current + 1;
_self._slideTo( pos );
if( _self.options.autoplay ) {
_self._startSlideshow();
}
}, this.options.slideshow_interval);
},
// shows the clicked thumb's slide
_slideTo : function( pos ) {
// return if clicking the same element or if currently animating
if( pos === this.current || this.isAnimating )
return false;
this.isAnimating = true;
var $currentSlide = this.$imgItems.eq( this.current ),
$nextSlide = this.$imgItems.eq( pos ),
_self = this,
preCSS = {zIndex : 10},
animCSS = {opacity : 1};
// new slide will slide in from left or right side
if( this.options.animation === 'sides' ) {
preCSS.left = ( pos > this.current ) ? -1 * this.elWidth : this.elWidth;
animCSS.left = 0;
}
// titles animation
$nextSlide.find('div.ei-title > h2')
.css( 'margin-right', 50 + 'px' )
.stop()
.delay( this.options.speed * this.options.titlesFactor )
.animate({ marginRight : 0 + 'px', opacity : 1 }, this.options.titlespeed, this.options.titleeasing )
.end()
.find('div.ei-title > h3')
.css( 'margin-right', -50 + 'px' )
.stop()
.delay( this.options.speed * this.options.titlesFactor )
.animate({ marginRight : 0 + 'px', opacity : 1 }, this.options.titlespeed, this.options.titleeasing )
$.when(
// fade out current titles
$currentSlide.css( 'z-index' , 1 ).find('div.ei-title > *').stop().fadeOut( this.options.speed / 2, function() {
// reset style
$(this).show().css( 'opacity', 0 );
}),
// animate next slide in
$nextSlide.css( preCSS ).stop().animate( animCSS, this.options.speed, this.options.easing ),
// "sliding div" moves to new position
this.$sliderElem.stop().animate({
left : this.$thumbs.eq( pos ).position().left
}, this.options.speed )
).done( function() {
// reset values
$currentSlide.css( 'opacity' , 0 ).find('div.ei-title > *').css( 'opacity', 0 );
_self.current = pos;
_self.isAnimating = false;
});
},
_initEvents : function() {
var _self = this;
// window resize
$(window).on( 'smartresize.eislideshow', function( event ) {
// resize the images
_self._setImagesSize();
// reset position of thumbs sliding div
_self.$sliderElem.css( 'left', _self.$thumbs.eq( _self.current ).position().left );
});
// click the thumbs
this.$thumbs.on( 'click.eislideshow', function( event ) {
if( _self.options.autoplay ) {
clearTimeout( _self.slideshow );
_self.options.autoplay = false;
}
var $thumb = $(this),
idx = $thumb.index() - 1; // exclude sliding div
_self._slideTo( idx );
return false;
});
}
};
var logError = function( message ) {
if ( this.console ) {
console.error( message );
}
};
$.fn.eislideshow = function( options ) {
if ( typeof options === 'string' ) {
var args = Array.prototype.slice.call( arguments, 1 );
this.each(function() {
var instance = $.data( this, 'eislideshow' );
if ( !instance ) {
logError( "cannot call methods on eislideshow prior to initialization; " +
"attempted to call method '" + options + "'" );
return;
}
if ( !$.isFunction( instance[options] ) || options.charAt(0) === "_" ) {
logError( "no such method '" + options + "' for eislideshow instance" );
return;
}
instance[ options ].apply( instance, args );
});
}
else {
this.each(function() {
var instance = $.data( this, 'eislideshow' );
if ( !instance ) {
$.data( this, 'eislideshow', new $.Slideshow( options, this ) );
}
});
}
return this;
};
})( window, jQuery );
How I can edit that to make them responsive ? can you give an example ?
I suggest you use something more simple (only 3 steps) instead of review this code.
Code:
Step 1: Link required files
First and most important, the jQuery library needs to be included (no need to download - link directly from Google). Next, download the package from this site and link the bxSlider CSS file (for the theme) and the bxSlider Javascript file.
<!-- jQuery library (served from Google) -->
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.8.2/jquery.min.js"></script>
<!-- bxSlider Javascript file -->
<script src="/js/jquery.bxslider.min.js"></script>
<!-- bxSlider CSS file -->
<link href="/lib/jquery.bxslider.css" rel="stylesheet" />
Step 2: Create HTML markup
Create a <ul class="bxslider"> element, with a <li> for each slide. Slides can contain images, video, or any other HTML content!
<ul class="bxslider">
<li><img src="/images/pic1.jpg" /></li>
<li><img src="/images/pic2.jpg" /></li>
<li><img src="/images/pic3.jpg" /></li>
<li><img src="/images/pic4.jpg" /></li>
</ul>
Step 3: Call the bxSlider
Call .bxslider() on <ul class="bxslider">. Note that the call must be made inside of a $(document).ready() call, or the plugin will not work!
$(document).ready(function(){
$('.bxslider').bxSlider();
});
I really believe, this will be more helpful and simple.
Credits: http://bxslider.com
Different configurations
$('.bxslider').bxSlider({
mode: 'fade',
captions: true,
pagerCustom: '#bx-pager',
adaptiveHeight: true,
slideWidth: 150
});
Don't forget to read this: http://bxslider.com/options

Why can't I draw elements to the stage?

Fiddle - http://liveweave.com/JS9EBN
This is a starter template for web design applications. (well almost except for the drawing problem)
Elements are drawn to the stage like so.
// Handles Drawable Elements
$("#canvas").on('mousedown touchstart', function(e) {
if(drawable) {
drawing = true;
mS.x = e.pageX;
mS.y = e.pageY;
dBox = $("<" + $('.draw-elements input[type=radio]:checked').val() + " class='box' />")
.html("Certains textes");
$(this).append(dBox);
// Do not select text when drawing
return false;
}
});
$(document).on('mousemove touchmove', function(e) {
if(drawing && drawable){
var mPos = {x:e.pageX, y:e.pageY};
var css = {};
css.position = 'absolute';
css.left = (mPos.x > mS.x) ? mS.x : mPos.x;
css.top = (mPos.y > mS.y) ? mS.y : mPos.y;
css.width = Math.abs(mPos.x - mS.x);
css.height = Math.abs(mPos.y - mS.y);
css.border = '1px dotted rgb(0, 34, 102)';
dBox.css(css);
// Do not select text when drawing
return false;
}
}).on('mouseup touchend', function(e) {
drawing = false;
});
As long as my select tool is not called I can draw elements without a problem, but when it is called and I come back to draw a div I can no longer draw elements to my stage.
After some tinkering I noticed the problem resides with my drag function for each element that's selected.
var HandleSelectedElement = function() {
if ($(".select-tool.activetool").is(":visible")) {
if(elmstyle) {
$('#canvas').children().drag("start",function( ev, dd ){
dd.attrc = $( ev.target ).prop("className");
dd.attrd = $( ev.target ).prop("id");
dd.width = $( this ).width();
dd.height = $( this ).height();
})
.drag(function( ev, dd ){
var props = {};
if ( dd.attrc.indexOf("E") > -1 ){
props.width = Math.max( 32, dd.width + dd.deltaX );
}
if ( dd.attrc.indexOf("S") > -1 ){
props.height = Math.max( 32, dd.height + dd.deltaY );
}
if ( dd.attrc.indexOf("W") > -1 ){
props.width = Math.max( 32, dd.width - dd.deltaX );
props.left = dd.originalX + dd.width - props.width;
}
if ( dd.attrc.indexOf("N") > -1 ){
props.height = Math.max( 32, dd.height - dd.deltaY );
props.top = dd.originalY + dd.height - props.height;
}
if ( dd.attrd.indexOf("stylethis") > -1 ){
props.top = dd.offsetY;
props.left = dd.offsetX;
}
$('#stylethis').css( props );
}, {relative:true});
}
};
I don't understand what's wrong.
I couldn't find out how to solve the initial problem. So I decided to do a work around.
Here's the fiddle: http://liveweave.com/g2aKlD
I decided to refresh the canvas each time a tool is clicked. I take the canvas's html and set that as a textarea's value. I then set the textarea's value as the canvas's html.
$("#code").val($("#canvas").html());
$("#canvas").html($("#code").val());
This way whatever called the initial bug is removed completely. However last time I did this all spaces where turned into a otherwise known as a non-breakable space (I've also seen this also add unnecessary line breaks and paragraphs when not needed)
I still don't understand why my draw function did not work when drag was turned off.
I really hate having to do this (being refreshing the canvas) because the more elements that are in there and the more refreshing is done, the more ram it uses which makes the browser work the cpu that much harder.
I hope someone can come across a better solution.

Jquery splitter plugin getting error too much recursion

I am using this jquery splitter plugin located here: http://methvin.com/splitter/
It is working fine with the version of jquery I am using until I enable the resizeToWidth property then it is giving me the error: too much recursion.
Here is a link to a demo I created on jsfiddle: http://jsfiddle.net/S97rv/4/
Iv looked at the plugin code but im not a javascript expert and don't want to mess with it to much.
Can anybody see a solution to this error?
Here is the plugin code but probably better just looking at the jsfiddle link:
;(function($){
$.fn.splitter = function(args){
args = args || {};
return this.each(function() {
var zombie; // left-behind splitbar for outline resizes
function startSplitMouse(evt) {
if ( opts.outline )
zombie = zombie || bar.clone(false).insertAfter(A);
panes.css("-webkit-user-select", "none"); // Safari selects A/B text on a move
bar.addClass(opts.activeClass);
A._posSplit = A[0][opts.pxSplit] - evt[opts.eventPos];
$(document)
.bind("mousemove", doSplitMouse)
.bind("mouseup", endSplitMouse);
}
function doSplitMouse(evt) {
var newPos = A._posSplit+evt[opts.eventPos];
if ( opts.outline ) {
newPos = Math.max(0, Math.min(newPos, splitter._DA - bar._DA));
bar.css(opts.origin, newPos);
} else
resplit(newPos);
}
function endSplitMouse(evt) {
bar.removeClass(opts.activeClass);
var newPos = A._posSplit+evt[opts.eventPos];
if ( opts.outline ) {
zombie.remove(); zombie = null;
resplit(newPos);
}
panes.css("-webkit-user-select", "text"); // let Safari select text again
$(document)
.unbind("mousemove", doSplitMouse)
.unbind("mouseup", endSplitMouse);
}
function resplit(newPos) {
// Constrain new splitbar position to fit pane size limits
newPos = Math.max(A._min, splitter._DA - B._max,
Math.min(newPos, A._max, splitter._DA - bar._DA - B._min));
// Resize/position the two panes
bar._DA = bar[0][opts.pxSplit]; // bar size may change during dock
bar.css(opts.origin, newPos).css(opts.fixed, splitter._DF);
A.css(opts.origin, 0).css(opts.split, newPos).css(opts.fixed, splitter._DF);
B.css(opts.origin, newPos+bar._DA)
.css(opts.split, splitter._DA-bar._DA-newPos).css(opts.fixed, splitter._DF);
// IE fires resize for us; all others pay cash
if ( !$.browser.msie )
panes.trigger("resize");
}
function dimSum(jq, dims) {
// Opera returns -1 for missing min/max width, turn into 0
var sum = 0;
for ( var i=1; i < arguments.length; i++ )
sum += Math.max(parseInt(jq.css(arguments[i])) || 0, 0);
return sum;
}
// Determine settings based on incoming opts, element classes, and defaults
var vh = (args.splitHorizontal? 'h' : args.splitVertical? 'v' : args.type) || 'v';
var opts = $.extend({
activeClass: 'active', // class name for active splitter
pxPerKey: 8, // splitter px moved per keypress
tabIndex: 0, // tab order indicator
accessKey: '' // accessKey for splitbar
},{
v: { // Vertical splitters:
keyLeft: 39, keyRight: 37, cursor: "e-resize",
splitbarClass: "vsplitbar", outlineClass: "voutline",
type: 'v', eventPos: "pageX", origin: "left",
split: "width", pxSplit: "offsetWidth", side1: "Left", side2: "Right",
fixed: "height", pxFixed: "offsetHeight", side3: "Top", side4: "Bottom"
},
h: { // Horizontal splitters:
keyTop: 40, keyBottom: 38, cursor: "n-resize",
splitbarClass: "hsplitbar", outlineClass: "houtline",
type: 'h', eventPos: "pageY", origin: "top",
split: "height", pxSplit: "offsetHeight", side1: "Top", side2: "Bottom",
fixed: "width", pxFixed: "offsetWidth", side3: "Left", side4: "Right"
}
}[vh], args);
// Create jQuery object closures for splitter and both panes
var splitter = $(this).css({position: "relative"});
var panes = $(">*", splitter[0]).css({
position: "absolute", // positioned inside splitter container
"z-index": "1", // splitbar is positioned above
"-moz-outline-style": "none" // don't show dotted outline
});
var A = $(panes[0]); // left or top
var B = $(panes[1]); // right or bottom
// Focuser element, provides keyboard support; title is shown by Opera accessKeys
var focuser = $('')
.attr({accessKey: opts.accessKey, tabIndex: opts.tabIndex, title: opts.splitbarClass})
.bind($.browser.opera?"click":"focus", function(){ this.focus(); bar.addClass(opts.activeClass) })
.bind("keydown", function(e){
var key = e.which || e.keyCode;
var dir = key==opts["key"+opts.side1]? 1 : key==opts["key"+opts.side2]? -1 : 0;
if ( dir )
resplit(A[0][opts.pxSplit]+dir*opts.pxPerKey, false);
})
.bind("blur", function(){ bar.removeClass(opts.activeClass) });
// Splitbar element, can be already in the doc or we create one
var bar = $(panes[2] || '<div></div>')
.insertAfter(A).css("z-index", "100").append(focuser)
.attr({"class": opts.splitbarClass, unselectable: "on"})
.css({position: "absolute", "user-select": "none", "-webkit-user-select": "none",
"-khtml-user-select": "none", "-moz-user-select": "none"})
.bind("mousedown", startSplitMouse);
// Use our cursor unless the style specifies a non-default cursor
if ( /^(auto|default|)$/.test(bar.css("cursor")) )
bar.css("cursor", opts.cursor);
// Cache several dimensions for speed, rather than re-querying constantly
bar._DA = bar[0][opts.pxSplit];
splitter._PBF = $.boxModel? dimSum(splitter, "border"+opts.side3+"Width", "border"+opts.side4+"Width") : 0;
splitter._PBA = $.boxModel? dimSum(splitter, "border"+opts.side1+"Width", "border"+opts.side2+"Width") : 0;
A._pane = opts.side1;
B._pane = opts.side2;
$.each([A,B], function(){
this._min = opts["min"+this._pane] || dimSum(this, "min-"+opts.split);
this._max = opts["max"+this._pane] || dimSum(this, "max-"+opts.split) || 9999;
this._init = opts["size"+this._pane]===true ?
parseInt($.curCSS(this[0],opts.split)) : opts["size"+this._pane];
});
// Determine initial position, get from cookie if specified
var initPos = A._init;
if ( !isNaN(B._init) ) // recalc initial B size as an offset from the top or left side
initPos = splitter[0][opts.pxSplit] - splitter._PBA - B._init - bar._DA;
if ( opts.cookie ) {
if ( !$.cookie )
alert('jQuery.splitter(): jQuery cookie plugin required');
var ckpos = parseInt($.cookie(opts.cookie));
if ( !isNaN(ckpos) )
initPos = ckpos;
$(window).bind("unload", function(){
var state = String(bar.css(opts.origin)); // current location of splitbar
$.cookie(opts.cookie, state, {expires: opts.cookieExpires || 365,
path: opts.cookiePath || document.location.pathname});
});
}
if ( isNaN(initPos) ) // King Solomon's algorithm
initPos = Math.round((splitter[0][opts.pxSplit] - splitter._PBA - bar._DA)/2);
// Resize event propagation and splitter sizing
if ( opts.anchorToWindow ) {
// Account for margin or border on the splitter container and enforce min height
splitter._hadjust = dimSum(splitter, "borderTopWidth", "borderBottomWidth", "marginBottom");
splitter._hmin = Math.max(dimSum(splitter, "minHeight"), 20);
$(window).bind("resize", function(){
var top = splitter.offset().top;
var wh = $(window).height();
splitter.css("height", Math.max(wh-top-splitter._hadjust, splitter._hmin)+"px");
if ( !$.browser.msie ) splitter.trigger("resize");
}).trigger("resize");
}
else if ( opts.resizeToWidth && !$.browser.msie )
$(window).bind("resize", function(){
splitter.trigger("resize");
});
// Resize event handler; triggered immediately to set initial position
splitter.bind("resize", function(e, size){
// Custom events bubble in jQuery 1.3; don't get into a Yo Dawg
if ( e.target != this ) return;
// Determine new width/height of splitter container
splitter._DF = splitter[0][opts.pxFixed] - splitter._PBF;
splitter._DA = splitter[0][opts.pxSplit] - splitter._PBA;
// Bail if splitter isn't visible or content isn't there yet
if ( splitter._DF <= 0 || splitter._DA <= 0 ) return;
// Re-divvy the adjustable dimension; maintain size of the preferred pane
resplit(!isNaN(size)? size : (!(opts.sizeRight||opts.sizeBottom)? A[0][opts.pxSplit] :
splitter._DA-B[0][opts.pxSplit]-bar._DA));
}).trigger("resize" , [initPos]);
});
};
})(jQuery);
The plugin you are using is based on a old jQuery version. For some reason, an infinite recursion was introduced by jQuery 1.6, probably due to event bubbling. It seems like a resize event triggered on a specific DOM element follow the event propagation path, all the way to document.
In the resize event handler, you can add a test to prevent recursion:
$(window).bind("resize", function (e) {
if (e.target === window) { splitter.trigger('resize'); }
});
That works, at least in Chrome and Firefox.
Using jQuery Migrate plugin will allow you to use jQuery 1.9 and even 2.0; but relying on browser specific behavior is generally a bad practice. You may have a look at this splitter.js fork, which already fixes your infinite recursion issue, using the same test as above.

Javascript `this` not working as I thought?

I'm adapting a pretty basic js function into a class. Anyway, basically it just creates a floating container
above the main page. I'm aware it's incomplete, but I'm in the middle of typing it up, and keep getting caught out when attempting to call the close() function. Firefox returns a this.sdiv is undefined. I'm confused as to how this can be the case when close() is Pop's method and sdiv is defined in the first line of the Pop class?
function Pop( wpx,hpx ){
Pop.prototype.sdiv;
Pop.prototype.shadow;
Pop.prototype.pdiv;
// start with the invisible screen, which
// covers the main page
this.sdiv = document.createElement("DIV")
this.sdiv.className = "popScreen";
this.sdiv.id = "popScreen";
// this screen covers the full document, so
// base dimensions on the document size
this.sdiv.style.width = document.body.clientWidth + "px";
this.sdiv.style.height = document.body.clientHeight + "px";
document.body.appendChild( this.sdiv );
// attach drop shadow
this.shadow = document.createElement("DIV");
this.shadow.className = "popShadow";
this.shadow.style.width = wpx + "px";
this.shadow.style.height = hpx + "px";
this.shadow.style.left = ( ( window.innerWidth / 2 ) - ( wpx / 2 ) ) + "px";
this.shadow.style.top = ( ( window.innerHeight / 2 ) - ( hpx / 2 ) ) + "px";
document.body.appendChild( this.shadow );
this.pdiv = document.createElement("DIV");
this.pdiv.className = "pop";
this.pdiv.id = "pop";
this.pdiv.style.position = "absolute";
this.pdiv.style.width = wpx + "px";
this.pdiv.style.height = hpx + "px";
this.shadow.appendChild( this.pdiv );
// bind an event to the screen div so that when it is clicked
// the Pop dialogue is closed and the user is return to the main page
$("div#popScreen").click( function( ){
Pop.prototype.close( );
} );
Pop.prototype.go = function( url, method, data ){
if( method == null )
$("div#pop").load( url );
}
Pop.prototype.close = function( ){
this.sdiv.parentNode.removeChild( this.sdiv );
this.shadow.parentNode.removeChild( this.shadow );
this.pdiv.parentNode.removeChild( this.pdiv );
}
}
Thanks in advance for any help
You can't use Pop.prototype.close() to close all Pop instances. Instead, for each instance of Pop that you have created with the new operator, you need to call popInstance.close().
this in javascript works very differently to this in oo languages.
The error you have is here:
Pop.prototype.close = function( ){
this.sdiv.parentNode.removeChild( this.sdiv );
this.shadow.parentNode.removeChild( this.shadow );
this.pdiv.parentNode.removeChild( this.pdiv );
}
In that function this is probably referring to the window (set a breakpoint and have a look in firebug)
Perhaps something along these lines will work.
var parent = this;
Pop.prototype.close = function(){
parent.sdiv.parentNode.removeChild( this.sdiv );
parent.shadow.parentNode.removeChild( this.shadow );
parent.pdiv.parentNode.removeChild( this.pdiv );
}

Categories

Resources