editing javascript nav buttons in a slider, previous and next - javascript

I am currently styling the nav buttons of hero slider using css, below is my code, however I want to achieve the buttons like on the slider on www.bbc.co.uk. With the expanding div and the text appearing. Could sombeody show me how please?
This is the css for the buttons I would like to edit
.hero-carousel-nav li {
position: absolute;
bottom: 48px;
right: 48px;
list-style: none;
}
.hero-carousel-nav li.prev {
left: 48px;
right: auto;
}
.hero-carousel-nav li a {
background: #FFF;
color: #fff;
border: none;
outline: none;
display: block;
float: left;
padding: 5px 20px;
-moz-border-radius: 20px;
-webkit-border-radius: 20px;
border-radius: 10px;
behavior: url(/assets/PIE.htc);
}
.hero-carousel-nav li a:hover {
background: #06C;
}
and this is my javascript which includes the coding for the previous and next nav buttons that I would like to edit.
jQuery.fn.heroCarousel = function(options){
options = jQuery.extend({
animationSpeed: 1000,
navigation: true,
easing: '',
timeout: 5000,
pause: true,
pauseOnNavHover: true,
prevText: 'Previous',
nextText: 'Next',
css3pieFix: false,
currentClass: 'current',
onLoad: function(){},
onStart: function(){},
onComplete: function(){}
}, options);
if(jQuery.browser.msie && parseFloat(jQuery.browser.version) < 7){
options.animationSpeed = 0;
}
return this.each(function() {
var carousel = jQuery(this),
elements = carousel.children();
currentItem = 1;
childWidth = elements.width();
childHeight = elements.height();
if(elements.length > 2){
elements.each(function(i){
if(options.itemClass){
jQuery(this).addClass(options.itemClass);
}
});
elements.filter(':first').addClass(options.currentClass).before(elements.filter(':last'));
var carouselWidth = Math.round(childWidth * carousel.children().length),
carouselMarginLeft = '-'+ Math.round(childWidth + Math.round(childWidth / 2) ) +'px'
carousel.addClass('hero-carousel-container').css({
'position': 'relative',
'overflow': 'hidden',
'left': '50%',
'top': 0,
'margin-left': carouselMarginLeft,
'height': childHeight,
'width': carouselWidth
});
carousel.before('<ul class="hero-carousel-nav"><li class="prev">'+options.prevText+'</li><li class="next">'+options.nextText+'</li></ul>');
var carouselNav = carousel.prev('.hero-carousel-nav'),
timeoutInterval;
if(options.timeout > 0){
var paused = false;
if(options.pause){
carousel.hover(function(){
paused = true;
},function(){
paused = false;
});
}
if(options.pauseOnNavHover){
carouselNav.hover(function(){
paused = true;
},function(){
paused = false;
});
}
function autoSlide(){
if(!paused){
carouselNav.find('.next a').trigger('click');
}
}
timeoutInterval = window.setInterval(autoSlide, options.timeout);
}
carouselNav.find('a').data('disabled', false).click(function(e){
e.preventDefault();
var navItem = jQuery(this),
isPrevious = navItem.parent().hasClass('prev'),
elements = carousel.children();
if(navItem.data('disabled') === false){
options.onStart(carousel, carouselNav, elements.eq(currentItem), options);
if(isPrevious){
animateItem(elements.filter(':last'), 'previous');
}else{
animateItem(elements.filter(':first'), 'next');
}
navItem.data('disabled', true);
setTimeout(function(){
navItem.data('disabled', false);
}, options.animationSpeed+200);
if(options.timeout > 0){
window.clearInterval(timeoutInterval);
timeoutInterval = window.setInterval(autoSlide, options.timeout);
}
}
});
function animateItem(object,direction){
var carouselPosLeft = parseFloat(carousel.position().left),
carouselPrevMarginLeft = parseFloat(carousel.css('margin-left'));
if(direction === 'previous'){
object.before( object.clone().addClass('carousel-clone') );
carousel.prepend( object );
var marginLeft = Math.round(carouselPrevMarginLeft - childWidth);
var plusOrMinus = '+=';
}else{
object.after( object.clone().addClass('carousel-clone') );
carousel.append( object );
var marginLeft = carouselMarginLeft;
var plusOrMinus = '-=';
}
if(options.css3pieFix){
fixPieClones(jQuery('.carousel-clone'));
}
carousel.css({
'left': carouselPosLeft,
'width': Math.round(carouselWidth + childWidth),
'margin-left': marginLeft
}).animate({'left':plusOrMinus+childWidth}, options.animationSpeed, options.easing, function(){
carousel.css({
'left': '50%',
'width': carouselWidth,
'margin-left': carouselPrevMarginLeft
});
jQuery('.carousel-clone').remove();
finishCarousel();
});
}
function fixPieClones(clonedObject){
var itemPieId = clonedObject.attr('_pieId');
if(itemPieId){
clonedObject.attr('_pieId', itemPieId+'_cloned');
}
clonedObject.find('*[_pieId]').each(function(i, item){
var descendantPieId = $(item).attr('_pieId');
$(item).attr('_pieId', descendantPieId+'_cloned');
});
}
function finishCarousel(){
var elements = carousel.children();
elements.removeClass(options.currentClass).eq(currentItem).addClass(options.currentClass);
options.onComplete(carousel, carousel.prev('.hero-carousel-nav'), elements.eq(currentItem), options);
}
if(jQuery.browser.msie){
carouselNav.find('a').attr("hideFocus", "true");
}
options.onLoad(carousel, carouselNav, carousel.children().eq(currentItem), options);
}
});
};

BBC website is actually using a custom javascript library called glow. you might want to look at this:
Alternatively you can also use this slider, by checking the source code.

Related

Jquery toggle icons on show more and less

I am using https://github.com/jasonujmaalvis/show-more to show and hide text on a mobile device. What I want to do is toggle between images on show more and show less:
So far I have:
Jquery:
source file:
;
(function($, window, document, undefined) {
'use strict';
var pluginName = 'showmore',
defaults = {
closedHeight: 100,
buttonTextMore: 'show more',
buttonTextLess: 'show less',
buttonCssClass: 'showmore-button',
animationSpeed: 0.5,
openHeightOffset: 0,
onlyWithWindowMaxWidth: 0
};
function Plugin(element, options) {
this.element = element;
this.settings = $.extend({}, defaults, options);
this._defaults = defaults;
this._name = pluginName;
this.btn;
this.init();
}
$.extend(Plugin.prototype, {
init: function() {
if (this.settings.onlyWithWindowMaxWidth > 0) {
this.bindResize();
this.responsive();
} else {
this.showmore();
}
},
bindResize: function() {
var self = this;
var resizeTimer;
$(window).on('resize', function() {
if (resizeTimer) {
clearTimeout(resizeTimer);
}
resizeTimer = setTimeout(function() {
self.responsive();
}, 250);
});
},
responsive: function() {
if ($(window).innerWidth() <= this.settings.onlyWithWindowMaxWidth) {
this.showmore();
} else {
this.remove();
}
},
showmore: function() {
if (this.btn) {
return;
}
var self = this;
var element = $(this.element);
var settings = this.settings;
if (settings.animationSpeed > 10) {
settings.animationSpeed = settings.animationSpeed / 1000;
}
var showMoreInner = $('<div />', {
'class': settings.buttonCssClass + '-inner more',
text: settings.buttonTextMore
});
var showLessInner = $('<div />', {
'class': settings.buttonCssClass + '-inner less',
text: settings.buttonTextLess
});
element.addClass('closed').css({
'height': settings.closedHeight,
'overflow': 'hidden'
});
var resizeTimer;
$(window).on('resize', function() {
if (!element.hasClass('closed')) {
if (resizeTimer) {
clearTimeout(resizeTimer);
}
resizeTimer = setTimeout(function() {
// resizing has "stopped"
self.setOpenHeight(true);
}, 150); // this must be less than bindResize timeout!
}
});
var showMoreButton = $('<div />', {
'class': settings.buttonCssClass,
html: showMoreInner
});
showMoreButton.on('click', function(event) {
event.preventDefault();
if (element.hasClass('closed')) {
self.setOpenHeight();
element.removeClass('closed');
showMoreButton.html(showLessInner);
} else {
element.css({
'height': settings.closedHeight,
'transition': 'all ' + settings.animationSpeed + 's ease'
}).addClass('closed');
showMoreButton.html(showMoreInner);
}
});
element.after(showMoreButton);
this.btn = showMoreButton;
},
setOpenHeight: function(noAnimation) {
$(this.element).css({
'height': this.getOpenHeight()
});
if (noAnimation) {
$(this.element).css({
'transition': 'none'
});
} else {
$(this.element).css({
'transition': 'all ' + this.settings.animationSpeed + 's ease'
});
}
},
getOpenHeight: function() {
$(this.element).css({'height': 'auto', 'transition': 'none'});
var targetHeight = $(this.element).innerHeight();
$(this.element).css({'height': this.settings.closedHeight});
// we must call innerHeight() otherwhise there will be no css animation
$(this.element).innerHeight();
return targetHeight;
},
remove: function() {
// var element = $(this.element);
if ($(this.element).hasClass('closed')) {
this.setOpenHeight();
}
if (this.btn) {
this.btn.off('click').empty().remove();
this.btn = undefined;
}
}
});
$.fn[pluginName] = function(options) {
return this.each(function() {
if (!$.data(this, 'plugin_' + pluginName)) {
$.data(this, 'plugin_' + pluginName, new Plugin(this, options));
}
});
};
})(jQuery, window, document);
my Jquery:
$('.read-more').showmore({
closedHeight: 100,
shadow: true,
onlyWithWindowMaxWidth: 576,
buttonCssClass: 'showmore-button',
buttonTextLess: 'Read less',
buttonTextMore: 'Read more'
});
CSS
.home-text .showmore-button {
margin-bottom: 25px;
background-image: url('../assets/images/plus-octagon-light.svg')!important;
background-repeat: no-repeat;
width: 150px;
padding-left: 40px;
height: 30px;
display: block;
}
.home-text .showmore-button::active {
margin-bottom: 25px;
background-image: url('../assets/images/minus-octagon-light.svg')!important;
background-repeat: no-repeat;
width: 150px;
padding-left: 40px;
height: 30px;
display: block;
}
.read-more { line-height:24px; }
.read-more_content { position:relative; overflow:hidden; }
.read-more_trigger { width:100%; height:45px; line-height:45px; cursor:pointer; }
.read-more_trigger span { display:block; }
html
<div class="home-text"><p>xxxxxxxx</p>
</div>
So I am trying to toggle between icons on show more and show less. Any ideas? I know if I used Jquery as stand alone functions I could use the toggle class, but as this is a JS plugin that is where I am thinking where to add it?
After viewing the JQuery code more, the solution that worked for me was changing the CSS with the following classes:
.showmore-button-inner.more {
margin-bottom: 25px;
background-image: url('../assets/images/plus-octagon-light.svg')!important;
background-repeat: no-repeat;
width: 150px;
padding-left: 40px;
height: 30px;
display: block;
}
.showmore-button-inner.less {
margin-bottom: 25px;
background-image: url('../assets/images/minus-octagon-light.svg')!important;
background-repeat: no-repeat;
width: 150px;
padding-left: 40px;
height: 30px;
display: block;
}

How to pass aspectRatio option to alsoResize Object in jQuery UI's resizable function?

Alright, to make it short, here you can see the problem:
http://jsfiddle.net/EgBh3/
When resizing the slider on the left side the video on the right is resizing too. It's not implemented in the jQuery UI that the other div size is inverted when resizing. I found this useful function here: jQuery UI Resizable alsoResize reverse
The problem is that the aspect ratio of the video changes. If you play it after resizing you see that the control bar is longer than the video itself for example.
The normal resizable() function offers the option to set an aspectRatio that should be kept. But is it possible to pass such an option to the alsoResizeReverse function? If yes, how?
Here's the code:
HTML:
<div id="wrapper">
<div id="pdf_presentation">
<div id="slider">
<ul class="bxslider">
<li class="12302">
<img src="http://www.projectorcentral.com/images/articles/4-3%20Bristlecone%20Pines.jpg" alt="page 0" />
</li>
<li class="12302">
<img src="http://img3.wikia.nocookie.net/__cb20060423235201/tovid/images/0/00/4_3_grid.jpg" alt="page 1" />
</li>
<li class="12302">
<img src="http://www.freecomputerdesktopwallpaper.com/new_wallpaper/5_4_3_2_1_Happy_New_Year_freecomputerdesktopwallpaper_p.jpg" alt="page 2" />
</li>
<li class="12302">
<img src="http://archive.dojotoolkit.org/nightly/dojotoolkit/dojox/image/tests/images/imageHoriz.jpg" alt="page 3" />
</li>
<li class="12302">
<img src="http://4.bp.blogspot.com/-cVEz-BBU5Mw/U8uLaEKhdCI/AAAAAAAAJxQ/ZG06K9VPj9A/s1600/P1130206.JPG" alt="page 4" />
</li>
<li class="12302">
<img src="https://encrypted-tbn2.gstatic.com/images?q=tbn:ANd9GcSKccu9ixyiNsVR7cHZNdQNUiTRwMMEvkGFbaU_eFC1FaW_DTMCNw" alt="page 5" />
</li>
</ul>
<div id="custom_pager">
<ul id="custom_pager_list">
<li>1</li>
<li>2</li>
<li>3</li>
<li>4</li>
<li>5</li>
</ul>
</div>
</div>
</div>
<div id="video_wrapper">
<video preload="none" id="movie" controls>
<source id="mp4" type="video/mp4" src="http://www.w3schools.com/html/mov_bbb.mp4"></source>
<source id="webm" type="video/ogg" src="http://www.w3schools.com/html/mov_bbb.ogg"></source>
</video>
</div>
</div>
CSS:
*{
padding: 0;
margin: 0;
}
body{
font-family: Arial, Helvetica, Verdana, sans-serif;
font-size: 12px;
}
#wrapper{
width: 980px;
height: 1000px;
margin: 0 auto;
margin-top: 20px;
box-shadow: 0 0 10px #ccc;
}
#slider{
width: 400px;
height: 300px;
float: left;
margin-left: 10px;
}
#video_wrapper{
float: right;
margin-right: 10px;
}
#video_wrapper video{
width: 548px;
height: 308px;
}
.bx-pager{
display: none;
}
#custom_pager{
float: left;
width: 400px;
height: auto;
}
#custom_pager li{
float: left;
list-style-type: none;
cursor: pointer;
padding: 0 3px;
}
#custom_pager li:hover {
font-weight: bold;
}
.bx-wrapper{
margin-bottom: 0 !important;
}
.current_slide{
font-weight: bold;
}
.ui-resizable-se{
z-index: 300000 !important;
}
Javascript/jQuery:
$('.bxslider').bxSlider({
mode: 'fade',
infiniteLoop: false,
hideControlOnEnd: true,
keyboardEnabled: true,
useCSS: false,
controls: false
});
$('#slider').resizable({
aspectRatio: 750 / 577,
handles: 'se',
containment: '#wrapper',
autoHide: true,
alsoResizeReverse: "#movie",
resize: function (e, ui) {
$('.bxslider img, .bx-viewport, .bx-wrapper').css({
width: ui.size.width,
height: ui.size.height
});
$('.bx-wrapper img').css({
maxWidth: ui.size.width
});
$('#custom_pager').css({
width: ui.size.width
});
//console.log($('#custom_pager').height());
var custom_pager_height = $('#custom_pager').height();
$('#slider').css({
height: ui.size.height + custom_pager_height
});
}
});
$.ui.plugin.add("resizable", "alsoResizeReverse", {
start: function(event, ui) {
var self = $(this).data("resizable"), o = self.options;
var _store = function(exp) {
$(exp).each(function() {
$(this).data("resizable-alsoresize-reverse", {
width: parseInt($(this).width(), 10), height: parseInt($(this).height(), 10),
left: parseInt($(this).css('left'), 10), top: parseInt($(this).css('top'), 10)
});
});
};
if (typeof(o.alsoResizeReverse) == 'object' && !o.alsoResizeReverse.parentNode) {
if (o.alsoResizeReverse.length) { o.alsoResize = o.alsoResizeReverse[0]; _store(o.alsoResizeReverse); }
else { $.each(o.alsoResizeReverse, function(exp, c) { _store(exp); }); }
}else{
_store(o.alsoResizeReverse);
}
},
resize: function(event, ui){
var self = $(this).data("resizable"), o = self.options, os = self.originalSize, op = self.originalPosition;
var delta = {
height: (self.size.height - os.height) || 0, width: (self.size.width - os.width) || 0,
top: (self.position.top - op.top) || 0, left: (self.position.left - op.left) || 0
},
_alsoResizeReverse = function(exp, c) {
$(exp).each(function() {
var el = $(this), start = $(this).data("resizable-alsoresize-reverse"), style = {}, css = c && c.length ? c : ['width', 'height', 'top', 'left'];
$.each(css || ['width', 'height', 'top', 'left'], function(i, prop) {
var sum = (start[prop]||0) - (delta[prop]||0); // subtracting instead of adding
if (sum && sum >= 0)
style[prop] = sum || null;
});
//Opera fixing relative position
if (/relative/.test(el.css('position')) && $.browser.opera) {
self._revertToRelativePosition = true;
el.css({ position: 'absolute', top: 'auto', left: 'auto' });
}
el.css(style);
});
};
if (typeof(o.alsoResizeReverse) == 'object' && !o.alsoResizeReverse.nodeType) {
$.each(o.alsoResizeReverse, function(exp, c) { _alsoResizeReverse(exp, c); });
}else{
_alsoResizeReverse(o.alsoResizeReverse);
}
},
stop: function(event, ui){
var self = $(this).data("resizable");
//Opera fixing relative position
if (self._revertToRelativePosition && $.browser.opera) {
self._revertToRelativePosition = false;
el.css({ position: 'relative' });
}
$(this).removeData("resizable-alsoresize-reverse");
}
});
By default, alsoResizeReverse does not have an option for setting an aspect ratio, but you can add one. Change your configuration of the resizable to include the option alsoResizeReverseAspectRatio:true:
$('#slider').resizable({
aspectRatio: 750 / 577,
handles: 'se',
containment: '#wrapper',
autoHide: true,
alsoResizeReverse: "#movie",
alsoResizeReverseAspectRatio:true,
resize: function (e, ui) {
$('.bxslider img, .bx-viewport, .bx-wrapper').css({
width: ui.size.width,
height: ui.size.height
});
$('.bx-wrapper img').css({
maxWidth: ui.size.width
});
$('#custom_pager').css({
width: ui.size.width
});
//console.log($('#custom_pager').height());
var custom_pager_height = $('#custom_pager').height();
$('#slider').css({
height: ui.size.height + custom_pager_height
});
}
});
And then change the code within the alsoResizeReverse modification to support this option. These lines:
$.each(css || ['width', 'height', 'top', 'left'], function(i, prop) {
var sum = (start[prop]||0) - (delta[prop]||0); // subtracting instead of adding
if (sum && sum >= 0)
style[prop] = sum || null;
});
should be changed to this:
if(o.alsoResizeReverseAspectRatio) {
$.each(css || ['width', 'top', 'left'], function(i, prop) {
var sum = (start[prop]||0) - (delta[prop]||0); // subtracting instead of adding
if (sum && sum >= 0)
style[prop] = sum || null;
});
// Handle height differently since we want it based on the original width
var sum;
if(o.alsoResizeReverseAspectRatio === true)
sum = (start['height']||0) - ((delta['width']||0) * start['height']/start['width']);
else
sum = (start['height']||0) - ((delta['width']||0) * 1 / o.alsoResizeReverseAspectRatio);
if(sum && sum >= 0)
style['height'] = sum || null;
} else {
$.each(css || ['width', 'height', 'top', 'left'], function(i, prop) {
var sum = (start[prop]||0) - (delta[prop]||0); // subtracting instead of adding
if (sum && sum >= 0)
style[prop] = sum || null;
});
}
The JSFiddle can be found here: http://jsfiddle.net/EgBh3/1/.
This worked for me. If I understand your question correctly, my issue was similar: I have a vertical screen-split which can be moved left-right and some images in the left view which should be resized accordingly - keeping the aspect ratio. In consequence, images are getting more "thumbnailed" when the screen is smaller (and more images can be seen then).
$( "#leftView" ).resizable({
alsoResizeKeepAspectRatio: ".divWithImage"
});
For that you have to add this custom plugin (it is an adapted version from the alsoResize plugin, taken form the jQueryui JS-file).
$.ui.plugin.add("resizable", "alsoResizeKeepAspectRatio", {
start: function() {
var that = $(this).resizable( "instance" ),
o = that.options;
$(o.alsoResizeKeepAspectRatio).each(function() {
var el = $(this);
el.data("ui-resizable-alsoresize", {
width: parseInt(el.width(), 10), height: parseInt(el.height(), 10),
left: parseInt(el.css("left"), 10), top: parseInt(el.css("top"), 10)
});
});
},
resize: function(event, ui) {
var that = $(this).resizable( "instance" ),
o = that.options,
os = that.originalSize,
op = that.originalPosition,
delta = {
height: (that.size.height - os.height) || 0,
width: (that.size.width - os.width) || 0,
top: (that.position.top - op.top) || 0,
left: (that.position.left - op.left) || 0
};
$(o.alsoResizeKeepAspectRatio).each(function() {
var el = $(this), start = $(this).data("ui-resizable-alsoresize"), style = {},
css = el.parents(ui.originalElement[0]).length ?
[ "width", "height" ] :
[ "width", "height", "top", "left" ];
$.each(css, function(i, prop) {
console.log("alsoResizeKeepAspectRatio i:"+i+" prop: "+prop+" h:"+el.height()+" w:"+el.width());
var deltaAR = 0;
if(prop == "width"){
deltaAR = delta[prop]
}
else // height --> This is the change in comparison to original
{
deltaAR=delta["width"]*el.height()/el.width();
}
var sum = (start[prop] || 0) + (deltaAR || 0);
if (sum && sum >= 0) {
style[prop] = sum || null;
}
});
// $.each(css, function(i, prop) {
// var sum = (start[prop] || 0) - (delta[prop] || 0);
// if (sum && sum >= 0) {
// style[prop] = sum || null;
// }
// });
el.css(style);
});
},
stop: function() {
$(this).removeData("resizable-alsoresize");
}
});

Interact with the tooltip in jQuery

How can I interact with the tooltip in jQuery?
You know, the little pop-up appearing when you hover an <a> element or an <img>.
I wanted to make that one follow my cursor when I move onto that tag. Exactly like this.
You might wanna look at jQuery UI's tooltip or the QTip plugin.
A part for mouse tracking tooltip: Mouse tracking
I didn't not tried it but it seems nice: one more
Here is simple jquery plugin for custom tooltip. jsFiddle
You can specify mouseFollow: true to achieve movable tooltip that follows cursor.
JS
(function ($) {
$.fn.tooltip = function (options) {
var defaults = {
background: '#fff',
border: '1px solid #999',
color: 'black',
rounded: false,
mouseFollow: false,
textChangeOnClick: false
},
settings = $.extend({}, defaults, options);
this.each(function () {
var $this = $(this),
title = null,
hovering = null;
//set class
if (!settings.textChangeOnClick) {
$this.addClass('_tooltip');
}
$(this).data('title', $this.attr('title'))
$(this).attr('title', '');
$this.hover(
// mouse over
function (e) {
//check change
if ($this.attr('title') != '') {
if ($this.attr('title') != $this.data('title')) {
$this.data('title', $this.attr('title'));
$this.attr('title','');
}
} else {
$this.removeAttr('title');
}
$this.attr('title', '');
hovering = true;
$('#tooltip').remove();
//create box
if ($this.data('title') != '') {
$('<div id="tooltip" />')
.appendTo('body')
.text($this.data('title'))
.hide()
.css({
backgroundColor: settings.background,
color: settings.color,
border: settings.border,
top: e.pageY + 10,
left: e.pageX + 20
})
.fadeIn(500);
}
if (settings.rounded) {
$('#tooltip').addClass('rounded');
}
},
//mouse out
function () {
hovering = false;
$('#tooltip').remove();
});
//text change
if (settings.textChangeOnClick) {
//on click
$this.on('click', function () {
if (hovering) {
//set new
$this.data('title',$(this).attr('title'));
$(this).attr('title', '');
$('#tooltip').text($this.data('title'));
}
});
}
//mouse follow
if (settings.mouseFollow) {
$this.mousemove(function (e) {
$('#tooltip').css({
top: e.pageY + 10,
left: e.pageX + 20
});
});
}
});
return this;
}
})(jQuery);
SET PLUGIN FOR ELEMENT
$('a').tooltip({
mouseFollow: true
});
HTML
CSS
#tooltip
{
border: 1px solid #BFBFBF;
float: left;
font-size: 11px;
max-width: 250px;
padding: 5px;
position: absolute;
color: #464646;
z-index: 999999;
}
.rounded
{
-moz-border-radius: 3px;
-webkit-border-radius: 3px;
}

Hook jQuery validation message changes

I want to display my jQuery validation messages in a tooltip. In order to accomplish this, I started out by adding the following CSS rules to my stylesheet:
fieldset .field-validation-error {
display: none;
}
fieldset .field-validation-error.tooltip-icon {
background-image: url('/content/images/icons.png');
background-position: -32px -192px;
width: 16px;
height: 16px;
display: inline-block;
}
and a very small piece of JS code:
; (function ($) {
$(function() {
var fields = $("fieldset .field-validation-valid, fieldset .field-validation-error");
fields.each(function() {
var self = $(this);
self.addClass("tooltip-icon");
self.attr("rel", "tooltip");
self.attr("title", self.text());
self.text("");
self.tooltip();
});
});
})(jQuery);
The issue is that I now need to capture any event when the validation message changes, I've been looking at the source for jquery.validate.unobtrusive.js, and the method I'd need to hook to is the function onError(error, inputElement) method.
My tooltip plugin works as long as I've an updated title attribute, the issue comes when the field is revalidated, and the validation message is regenerated, I would need to hook into that and prevent the message from being put out there and place it in the title attribute instead.
I want to figure out a way to do this without modifying the actual jquery.validate.unobtrusive.js file.
On a second note, how could I improve this in order to leave the functionality unaltered in case javascript is disabled?
Ok I went with this, just in case anyone runs into this again:
; (function ($) {
$(function() {
function convertValidationMessagesToTooltips(form) {
var fields = $("fieldset .field-validation-valid, fieldset .field-validation-error", form);
fields.each(function() {
var self = $(this);
self.addClass("tooltip-icon");
self.attr("rel", "tooltip");
self.attr("title", self.text());
var span = self.find("span");
if (span.length) {
span.text("");
} else {
self.text("");
}
self.tooltip();
});
}
$("form").each(function() {
var form = $(this);
var settings = form.data("validator").settings;
var old_error_placement = settings.errorPlacement;
var new_error_placement = function() {
old_error_placement.apply(settings, arguments);
convertValidationMessagesToTooltips(form);
};
settings.errorPlacement = new_error_placement;
convertValidationMessagesToTooltips(form); // initialize in case of model-drawn validation messages at page render time.
});
});
})(jQuery);
and styles:
fieldset .field-validation-error { /* noscript */
display: block;
margin-bottom: 20px;
}
fieldset .field-validation-error.tooltip-icon { /* javascript enabled */
display: inline-block;
margin-bottom: 0px;
background-image: url('/content/images/icons.png');
background-position: -32px -192px;
width: 16px;
height: 16px;
vertical-align: middle;
}
I'll just include the tooltip script I have, since it's kind of custom-made (though I based it off someone else's).
; (function ($, window) {
$.fn.tooltip = function (){
var classes = {
tooltip: "tooltip",
top: "tooltip-top",
left: "tooltip-left",
right: "tooltip-right"
};
function init(self, tooltip) {
if ($(window).width() < tooltip.outerWidth() * 1.5) {
tooltip.css("max-width", $(window).width() / 2);
} else {
tooltip.css("max-width", 340);
}
var pos = {
x: self.offset().left + (self.outerWidth() / 2) - (tooltip.outerWidth() / 2),
y: self.offset().top - tooltip.outerHeight() - 20
};
if (pos.x < 0) {
pos.x = self.offset().left + self.outerWidth() / 2 - 20;
tooltip.addClass(classes.left);
} else {
tooltip.removeClass(classes.left);
}
if (pos.x + tooltip.outerWidth() > $(window).width()) {
pos.x = self.offset().left - tooltip.outerWidth() + self.outerWidth() / 2 + 20;
tooltip.addClass(classes.right);
} else {
tooltip.removeClass(classes.right);
}
if (pos.y < 0) {
pos.y = self.offset().top + self.outerHeight();
tooltip.addClass(classes.top);
} else {
tooltip.removeClass(classes.top);
}
tooltip.css({
left: pos.x,
top: pos.y
}).animate({
top: "+=10",
opacity: 1
}, 50);
};
function activate() {
var self = $(this);
var message = self.attr("title");
var tooltip = $("<div class='{0}'></div>".format(classes.tooltip));
if (!message) {
return;
}
self.removeAttr("title");
tooltip.css("opacity", 0).html(message).appendTo("body");
var reload = function() { // respec tooltip's size and position.
init(self, tooltip);
};
reload();
$(window).resize(reload);
var remove = function () {
tooltip.animate({
top: "-=10",
opacity: 0
}, 50, function() {
$(this).remove();
});
self.attr("title", message);
};
self.bind("mouseleave", remove);
tooltip.bind("click", remove);
};
return this.each(function () {
var self = $(this);
self.bind("mouseenter", activate);
});
};
$.tooltip = function() {
return $("[rel~=tooltip]").tooltip();
};
})(jQuery, window);

JavaScript Modal is Too Big for Screen

I have created a modal window using the Prototype JavaScript framework and the following object:
var Modal = Class.create({
animate: function () {
this.step = (this.step + 1) % 31;
if (this.throbber) {
this.throbber.setStyle({
backgroundPosition: 'center ' + this.step * -33 + 'px'
});
}
},
destroy: function () {
if (this.interval_id) {
window.clearInterval(this.interval_id);
}
if (this.timeout_id) {
window.clearTimeout(this.timeout_id);
}
if (this.overlay.parentNode) {
this.overlay.remove();
}
if(this.window.select('select')){
this.window.select('select').each(function(ele){
Element.remove(ele);
});
}
this.window.select('*').invoke('remove');
if (this.window.parentNode) {
this.window.remove();
}
},
initialize: function (element) {
this.launch_element = element;
this.overlay = new Element('div', {'class': 'modal_overlay'});
$$('body').first().insert(this.overlay);
this.close = new Element('a', {
'class': 'modal_close'
}).insert('Close');
this.close.observe('click', this.destroy.bind(this));
this.window = new Element('div', {'class': 'modal_window'});
if(this.window.select('select')){
this.window.select('select').each(function(ele){
Element.remove(ele);
});
}
this.window.select('*').invoke('remove');
this.window.insert(this.close);
this.section = new Element('div', {'class': 'section'});
this.show_throbber();
this.window.insert(this.section);
$$('body').first().observe('keypress', function (e) {
var key_code = window.event ? event.keyCode : e.keyCode;
var esc = window.event ? 27 : e.DOM_VK_ESCAPE;
if (key_code === esc) {
this.destroy();
}
}.bind(this));
$$('.container').first().insert(this.window);
if (Prototype.Browser.IE) {
this.scroll_window();
Event.observe(window, 'scroll', this.scroll_window.bind(this));
}
},
remove_throbber: function () {
if (this.interval_id) {
window.clearInterval(this.interval_id);
}
if (this.timeout_id) {
window.clearTimeout(this.timeout_id);
}
this.throbber.remove();
},
scroll_window: function() {
var height, offsets;
offsets = document.viewport.getScrollOffsets();
this.overlay.setStyle({
left: offsets.left + 'px',
top: offsets.top + 'px'
});
height = document.viewport.getHeight();
this.window.setStyle({
top: offsets.top + Math.round(17 * height / 100) + 'px'
});
},
show_throbber: function (text) {
if(this.section.select('select')){
this.section.select('select').each(function(ele){
Element.remove(ele);
});
}
this.section.select('*').invoke('remove');
if (!text) {
text = 'Loading';
}
this.throbber = new Element('div', {
'class' : 'modal_throbber'
}).insert(new Element('p').insert(text + '...'));
this.section.insert(this.throbber);
this.step = 0;
this.interval_id = window.setInterval(this.animate.bind(this), 50);
this.complete = false;
this.timeout_id = window.setTimeout(this.timeout.bind(this), 20000);
},
timeout: function () {
var div, p, span;
if (!this.complete) {
if (this.interval_id) {
window.clearInterval(this.interval_id);
}
if (this.throbber) {
this.remove_throbber();
span = new Element('span', {'class': 'icon icon-delete'});
p = new Element('p', {'class': 'first'}).update(span);
p.insert('There seems to be something wrong with eSP. ' +
'Please try again later.');
div = new Element('div', {'class': 'error'}).update(p);
this.section.update(div);
}
if (this.timeout_id) {
window.clearTimeout(this.timeout_id);
}
}
}
});
and is styled with the following stylesheet:
.modal_overlay {
height: 100%;
width: 100%;
position: fixed;
left: 0;
top: 0;
z-index: 2999;
opacity: 0.5;
background: #000;
}
* html .modal_overlay {
filter: alpha(opacity=50);
position: absolute;
zoom: 1;
}
.modal_window {
display: block;
position: fixed;
top: 17%;
z-index: 3000;
}
* html .modal_window {
position: absolute;
}
.modal_close {
background: url('/images/close.gif') no-repeat scroll 0 0;
height: 26px;
cursor: pointer;
position: absolute;
right: -13px;
top: -8px;
width: 26px;
text-indent: -10000px;
}
.modal_throbber {
background: url('/images/throbber.png') no-repeat top center;
padding-top: 32px;
}
.modal_throbber p {
background: #fff;
text-align: center;
}
We are now getting reports from customers that when the modal window is taller than the screen they cannot scroll down to see the bottom part of the modal window's content.
Is it a case of us making sure that too much information isn't displayed in the modal or is there a better, more technical fix?
You can put a max-height on the modal window and overflow:scroll.

Categories