Is it possible to customize the fancybox HTML code?
I want to move out the next and previous buttons outside the .fancybox-outer
In order to move out the next and previous buttons of the .fancybox-outer container, you just need to add these CSS rules (in your own custom CSS file and AFTER you loaded the fancybox CSS file)
.fancybox-nav {
width: 60px;
}
.fancybox-nav span {
visibility: visible;
opacity: 0.5;
}
.fancybox-nav:hover span {
opacity: 1;
}
.fancybox-next {
right: -60px;
}
.fancybox-prev {
left: -60px;
}
You said you have looked around in the documentation but haven't found anything but the above is well documented in the fancyapps.com website here --> http://fancyapps.com/fancybox/#useful check No. 6, and the demo is in this fiddle
... but wonder if you will consider this as a correct answer.
Here is how I solved it. I did not know before that you could extend/replace methods in jquery. But this new method will (with some basic css) slide in from the right and out to the left.
It renders the buttons outside of the fancybox-outer element and it takes the width of the documen. It can probably be optimized a lot.
jQuery('ul a').fancybox({
margin: 0,
openMethod : 'afterZoomIn',
nextMethod : 'slideIn',
nextSpeed : 1000,
prevMethod : 'slideOut',
prevSpeed : 1000
});
(function ($, F) {
var getScalar = function(value, dim) {
var value_ = parseInt(value, 10);
if (dim && isPercentage(value)) {
value_ = F.getViewport()[ dim ] / 100 * value_;
}
return Math.ceil(value_);
},
getValue = function(value, dim) {
return getScalar(value, dim) + 'px';
};
F.transitions.afterZoomIn = function () {
var current = F.current;
if (!current) {
return;
}
F.isOpen = F.isOpened = true;
F.wrap.addClass('fancybox-opened').css('overflow', 'visible');
F.reposition();
// Assign a click event
if (current.closeClick || current.nextClick) {
F.inner.css('cursor', 'pointer').bind('click.fb', function(e) {
if (!$(e.target).is('a') && !$(e.target).parent().is('a')) {
F[ current.closeClick ? 'close' : 'next' ]();
}
});
}
// Create a close button
if (current.closeBtn) {
$(current.tpl.closeBtn).appendTo('.fancybox-overlay').bind('click.fb', F.close);
}
// Create navigation arrows
if (current.arrows && F.group.length > 1) {
if (current.loop || current.index > 0) {
$(current.tpl.prev).appendTo('.fancybox-overlay').bind('click.fb', F.prev);
}
if (current.loop || current.index < F.group.length - 1) {
$(current.tpl.next).appendTo('.fancybox-overlay').bind('click.fb', F.next);
}
}
F.trigger('afterShow');
// Stop the slideshow if this is the last item
if (!current.loop && current.index === current.group.length - 1) {
F.play( false );
} else if (F.opts.autoPlay && !F.player.isActive) {
F.opts.autoPlay = false;
F.play();
}
};
F.transitions.slideIn = function () {
var current = F.current,
effect = current.nextEffect,
startPos = current.pos,
endPos = { opacity : 1 },
direction = F.direction,
distance = $(document).width(),
field;
startPos.opacity = 0.1;
field = 'left';
if (direction === 'right') {
startPos[ field ] = getValue(getScalar(startPos[ field ]) - distance);
endPos[ field ] = '+=' + distance + 'px';
} else {
startPos[ field ] = getValue(getScalar(startPos[ field ]) + distance);
endPos[ field ] = '-=' + distance + 'px';
}
// Workaround for http://bugs.jquery.com/ticket/12273
if (effect === 'none') {
F._afterZoomIn();
} else {
F.wrap.css(startPos).animate(endPos, {
duration : current.nextSpeed,
easing : current.nextEasing,
complete : F._afterZoomIn
});
}
};
F.transitions.slideOut = function () {
var previous = F.previous,
effect = previous.prevEffect,
endPos = { opacity : 0 },
direction = F.direction,
distance = $(document).width();
if (effect === 'elastic') {
endPos[ 'left' ] = ( direction === 'left' ? '-' : '+' ) + '=' + distance + 'px';
}
previous.wrap.animate(endPos, {
duration : effect === 'none' ? 0 : previous.prevSpeed,
easing : previous.prevEasing,
complete : function () {
// $(this).trigger('onReset').remove();
previous.wrap.trigger('onReset').remove();
}
});
}
}(jQuery, jQuery.fancybox));
Related
Hy there! I plan to use Rater.js in my project and so far I am happy with the library. There is only one issue I can't resolve alone. Pls. take a look at
https://jsfiddle.net/herbert_hinterberger/r1cgnpt3/.
At the very bottom of the script, I defined step_size: 1.
(".rating").rate();
//or for example
var options = {
max_value: 5,
step_size: 1,
}
Now I would expect that when I hover over the stars, only full stars are selected. But still, half stars are selected too. Any Idea what I a doing wrong?
In your example code, you are calling $(".rating").rate(); without options (line number 499).
After commenting it, you will end-up with something like below
/*
* A highly customizable rating widget that supports images, utf8 glyphs and other html elements!
* https://github.com/auxiliary/rater
*/
; (function ($, window) {
$.fn.textWidth = function () {
var html_calc = $('<span>' + $(this).html() + '</span>');
html_calc.css('font-size', $(this).css('font-size')).hide();
html_calc.prependTo('body');
var width = html_calc.width();
html_calc.remove();
if (width == 0) {
var total = 0;
$(this).eq(0).children().each(function () {
total += $(this).textWidth();
});
return total;
}
return width;
};
$.fn.textHeight = function () {
var html_calc = $('<span>' + $(this).html() + '</span>');
html_calc.css('font-size', $(this).css('font-size')).hide();
html_calc.prependTo('body');
var height = html_calc.height();
html_calc.remove();
return height;
};
/*
* IE8 doesn't support isArray!
*/
Array.isArray = function (obj) {
return Object.prototype.toString.call(obj) === "[object Array]";
};
/*
* Utf-32 isn't supported by default, so we have to use Utf-8 surrogates
*/
String.prototype.getCodePointLength = function () {
return this.length - this.split(/[\uD800-\uDBFF][\uDC00-\uDFFF]/g).length + 1;
};
String.fromCodePoint = function () {
var chars = Array.prototype.slice.call(arguments);
for (var i = chars.length; i-- > 0;) {
var n = chars[i] - 0x10000;
if (n >= 0)
chars.splice(i, 1, 0xD800 + (n >> 10), 0xDC00 + (n & 0x3FF));
}
return String.fromCharCode.apply(null, chars);
};
/*
* Starting the plugin itself
*/
$.fn.rate = function (options) {
if (options === undefined || typeof options === 'object') {
return this.each(function () {
if (!$.data(this, "rate")) {
$.data(this, "rate", new Rate(this, options));
}
});
}
else if (typeof options === 'string') {
var args = arguments;
var returns;
this.each(function () {
var instance = $.data(this, "rate");
if (instance instanceof Rate && typeof instance[options] === 'function') {
returns = instance[options].apply(instance, Array.prototype.slice.call(args, 1));
}
if (options === 'destroy') {
// Unbind all events and empty the plugin data from instance
$(instance.element).off();
$.data(this, 'rate', null);
}
});
return returns !== undefined ? returns : this;
}
};
function Rate(element, options) {
this.element = element;
this.settings = $.extend({}, $.fn.rate.settings, options);
this.set_faces = {}; // value, symbol pairs
this.build();
}
Rate.prototype.build = function () {
this.layers = {};
this.value = 0;
this.raise_select_layer = false;
if (this.settings.initial_value) {
this.value = this.settings.initial_value;
}
if ($(this.element).attr("data-rate-value")) {
this.value = $(this.element).attr("data-rate-value");
}
/*
* Calculate the selected width based on the initial value
*/
var selected_width = this.value / this.settings.max_value * 100;
/*
* Let's support single strings as symbols as well as objects
*/
if (typeof this.settings.symbols[this.settings.selected_symbol_type] === 'string') {
var symbol = this.settings.symbols[this.settings.selected_symbol_type];
this.settings.symbols[this.settings.selected_symbol_type] = {};
this.settings.symbols[this.settings.selected_symbol_type]['base'] = symbol;
this.settings.symbols[this.settings.selected_symbol_type]['selected'] = symbol;
this.settings.symbols[this.settings.selected_symbol_type]['hover'] = symbol;
}
/*
* Making the three main layers (base, select, hover)
*/
var base_layer = this.addLayer("base-layer", 100, this.settings.symbols[
this.settings.selected_symbol_type]["base"], true);
var select_layer = this.addLayer("select-layer", selected_width,
this.settings.symbols[this.settings.selected_symbol_type]["selected"], true);
var hover_layer = this.addLayer("hover-layer", 0, this.settings.symbols[
this.settings.selected_symbol_type]["hover"], false);
/* var face_layer = this.addLayer("face-layer", 1, this.settings
.symbols[this.settings.face_layer_symbol_type][0], true); */
this.layers["base_layer"] = base_layer;
this.layers["select_layer"] = select_layer;
this.layers["hover_layer"] = hover_layer;
/*
* Bind the container to some events
*/
$(this.element).on("mousemove", $.proxy(this.hover, this));
$(this.element).on("click", $.proxy(this.select, this));
$(this.element).on("mouseleave", $.proxy(this.mouseout, this));
/*
* Set the main element as unselectable
*/
$(this.element).css({
"-webkit-touch-callout": "none",
"-webkit-user-select": "none",
"-khtml-user-select": "none",
"-moz-user-select": "none",
"-ms-user-select": "none",
"user-select": "none",
});
/*
* Update custom input field if provided
*/
if (this.settings.hasOwnProperty("update_input_field_name")) {
this.settings.update_input_field_name.val(this.value);
}
}
/*
* Function to add a layer
*/
Rate.prototype.addLayer = function (layer_name, visible_width, symbol, visible) {
var layer_body = "<div>";
for (var i = 0; i < this.settings.max_value; i++) {
if (Array.isArray(symbol)) {
if (this.settings.convert_to_utf8) {
symbol[i] = String.fromCodePoint(symbol[i]);
}
layer_body += "<span>" + (symbol[i]) + "</span>";
}
else {
if (this.settings.convert_to_utf8) {
symbol = String.fromCodePoint(symbol);
}
layer_body += "<span>" + symbol + "</span>";
}
}
layer_body += "</div>";
var layer = $(layer_body).addClass("rate-" + layer_name).appendTo(this.element);
$(layer).css({
width: visible_width + "%",
height: $(layer).children().eq(0).textHeight(),
overflow: 'hidden',
position: 'absolute',
top: 0,
display: visible ? 'block' : 'none',
'white-space': 'nowrap'
});
$(this.element).css({
width: $(layer).textWidth() + "px",
height: $(layer).height(),
position: 'relative',
cursor: this.settings.cursor,
});
return layer;
}
Rate.prototype.updateServer = function () {
if (this.settings.url != undefined) {
$.ajax({
url: this.settings.url,
type: this.settings.ajax_method,
data: $.extend({}, { value: this.getValue() }, this.settings.additional_data),
success: $.proxy(function (data) {
$(this.element).trigger("updateSuccess", [data]);
}, this),
error: $.proxy(function (jxhr, msg, err) {
$(this.element).trigger("updateError", [jxhr, msg, err]);
}, this)
});
}
}
Rate.prototype.getValue = function () {
return this.value;
}
Rate.prototype.hover = function (ev) {
var pad = parseInt($(this.element).css("padding-left").replace("px", ""));
var x = ev.pageX - $(this.element).offset().left - pad;
var val = this.toValue(x, true);
if (val != this.value) {
this.raise_select_layer = false;
}
if (!this.raise_select_layer && !this.settings.readonly) {
var visible_width = this.toWidth(val);
this.layers.select_layer.css({ display: 'none' });
if (!this.settings.only_select_one_symbol) {
this.layers.hover_layer.css({
width: visible_width + "%",
display: 'block'
});
}
else {
var index_value = Math.floor(val);
this.layers.hover_layer.css({
width: "100%",
display: 'block'
});
this.layers.hover_layer.children("span").css({
visibility: 'hidden',
});
this.layers.hover_layer.children("span").eq(index_value != 0 ? index_value - 1 : 0).css({
visibility: 'visible',
});
}
}
}
/*
* Event for when a rating has been selected (clicked)
*/
Rate.prototype.select = function (ev) {
if (!this.settings.readonly) {
var old_value = this.getValue();
var pad = parseInt($(this.element).css("padding-left").replace("px", ""));
var x = ev.pageX - $(this.element).offset().left - pad;
var selected_width = this.toWidth(this.toValue(x, true));
this.setValue(this.toValue(selected_width));
this.raise_select_layer = true;
}
}
Rate.prototype.mouseout = function () {
this.layers.hover_layer.css({ display: 'none' });
this.layers.select_layer.css({ display: 'block' });
}
/*
* Takes a width (px) and returns the value it resembles
*/
Rate.prototype.toWidth = function (val) {
return val / this.settings.max_value * 100;
}
/*
* Takes a value and calculates the width of the selected/hovered layer
*/
Rate.prototype.toValue = function (width, in_pixels) {
var val;
if (in_pixels) {
val = width / this.layers.base_layer.textWidth() * this.settings.max_value;
}
else {
val = width / 100 * this.settings.max_value;
}
// Make sure the division doesn't cause some small numbers added by
// comparing to a small arbitrary number.
var temp = val / this.settings.step_size;
if (temp - Math.floor(temp) < 0.00005) {
val = Math.round(val / this.settings.step_size) * this.settings.step_size;
}
val = (Math.ceil(val / this.settings.step_size)) * this.settings.step_size;
val = val > this.settings.max_value ? this.settings.max_value : val;
return val;
}
Rate.prototype.getElement = function (layer_name, index) {
return $(this.element).find(".rate-" + layer_name + " span").eq(index - 1);
}
Rate.prototype.getLayers = function () {
return this.layers;
}
Rate.prototype.setFace = function (value, face) {
this.set_faces[value] = face;
}
Rate.prototype.setAdditionalData = function (data) {
this.settings.additional_data = data;
}
Rate.prototype.getAdditionalData = function () {
return this.settings.additional_data;
}
Rate.prototype.removeFace = function (value) {
delete this.set_faces[value];
}
Rate.prototype.setValue = function (value) {
if (!this.settings.readonly) {
if (value < 0) {
value = 0;
}
else if (value > this.settings.max_value) {
value = this.settings.max_value;
}
var old_value = this.getValue();
this.value = value;
/*
* About to change event, should support prevention later
*/
var change_event = $(this.element).trigger("change", {
"from": old_value,
"to": this.value
});
/*
* Set/Reset faces
*/
$(this.element).find(".rate-face").remove();
$(this.element).find("span").css({
visibility: 'visible'
});
var index_value = Math.ceil(this.value);
if (this.set_faces.hasOwnProperty(index_value)) {
var face = "<div>" + this.set_faces[index_value] + "</div>";
var base_layer_element = this.getElement('base-layer', index_value);
var select_layer_element = this.getElement('select-layer', index_value);
var hover_layer_element = this.getElement('hover-layer', index_value);
var left_pos = base_layer_element.textWidth() * (index_value - 1)
+ (base_layer_element.textWidth() - $(face).textWidth()) / 2;
$(face).appendTo(this.element).css({
display: 'inline-block',
position: 'absolute',
left: left_pos,
}).addClass("rate-face");
base_layer_element.css({
visibility: 'hidden'
});
select_layer_element.css({
visibility: 'hidden'
});
hover_layer_element.css({
visibility: 'hidden'
});
}
/*
* Set styles based on width and value
*/
if (!this.settings.only_select_one_symbol) {
var width = this.toWidth(this.value);
this.layers.select_layer.css({
display: 'block',
width: width + "%",
height: this.layers.base_layer.css("height")
});
this.layers.hover_layer.css({
display: 'none',
height: this.layers.base_layer.css("height")
});
}
else {
var width = this.toWidth(this.settings.max_value);
this.layers.select_layer.css({
display: 'block',
width: width + "%",
height: this.layers.base_layer.css("height")
});
this.layers.hover_layer.css({
display: 'none',
height: this.layers.base_layer.css("height")
});
this.layers.select_layer.children("span").css({
visibility: 'hidden',
});
this.layers.select_layer.children("span").eq(index_value != 0 ? index_value - 1 : 0).css({
visibility: 'visible',
});
}
// Update the data-rate-value attribute
$(this.element).attr("data-rate-value", this.value);
if (this.settings.change_once) {
this.settings.readonly = true;
}
this.updateServer();
/*
* After change event
*/
var change_event = $(this.element).trigger("afterChange", {
"from": old_value,
"to": this.value
});
/*
* Update custom input field if provided
*/
if (this.settings.hasOwnProperty("update_input_field_name")) {
this.settings.update_input_field_name.val(this.value);
}
}
}
Rate.prototype.increment = function () {
this.setValue(this.getValue() + this.settings.step_size);
}
Rate.prototype.decrement = function () {
this.setValue(this.getValue() - this.settings.step_size);
}
$.fn.rate.settings = {
max_value: 5,
step_size: 0.5,
initial_value: 0,
symbols: {
utf8_star: {
base: '\u2606',
hover: '\u2605',
selected: '\u2605',
},
utf8_hexagon: {
base: '\u2B21',
hover: '\u2B22',
selected: '\u2B22',
},
hearts: '♥',
fontawesome_beer: '<i class="fa fa-beer"></i>',
fontawesome_star: {
base: '<i class="fa fa-star-o"></i>',
hover: '<i class="fa fa-star"></i>',
selected: '<i class="fa fa-star"></i>',
},
utf8_emoticons: {
base: [0x1F625, 0x1F613, 0x1F612, 0x1F604],
hover: [0x1F625, 0x1F613, 0x1F612, 0x1F604],
selected: [0x1F625, 0x1F613, 0x1F612, 0x1F604],
},
},
selected_symbol_type: 'utf8_star', // Must be a key from symbols
convert_to_utf8: false,
cursor: 'default',
readonly: false,
change_once: false, // Determines if the rating can only be set once
only_select_one_symbol: false, // If set to true, only selects the hovered/selected symbol and nothing prior to it
ajax_method: 'POST',
additional_data: {}, // Additional data to send to the server
//update_input_field_name = some input field set by the user
};
}(jQuery, window));
//$(".rating").rate();
//or for example
var options = {
max_value: 5,
step_size: 1,
}
$(".rating").rate(options);
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="rating" data-rate-value=6></div>
You are specifying the options at an earlier stage in your code.
See here:
$.fn.rate.settings = {
max_value: 5,
step_size: 0.5,
I tested in your fiddle and it works when updated to 1 step_size.
i am running a javascript to scroll down the image when it is zoomed in but the scroller doesn't work
here is script
$(function(){
// Add a custom filter to recognize images from lorempixel (that don't end with ".jpg" or something similar)
$.tosrus.defaults.media.image = {
filterAnchors: function( $anchor ) {
return $anchor.attr( 'href' ).indexOf( 'cdn.shopify.com' ) > -1;
}
};
$('#example-2 a').tosrus({
pagination : {
add : true
},
caption : {
add : true
},
slides : {
scale : 'fill'
}
});
});
try this
$('#event-list').scroll(function() {
var curScroll = $(this)[0].scrollTop,
maxScroll = $(this)[0].scrollHeight - $(this).height();
console.log(curScroll, ' :: ', maxScroll);
if ((curScroll >= maxScroll - 200) && !loading) {
loading = true;
$(this)[0].scrollTop = curScroll;
$('.loading').fadeIn('fast');
if (page <= $('.page').length) {
loadMore();
}
}
});
I have a plugin I've written for the purpose of showing/hiding tooltips. It works to the point that I'm happy enough to use it in production work, in the case when all I need is for it to show/hide the tooltip on hover. However I'd like to now alter it to show/hide on click as well.
I can get it to work up to a point but depending on what I try it either doesn't do one of the following: when I click from one tooltip activating element to another the previous tooltip doesn't hide - or - when I click from one element to another the previous tooltip closes but the next tooltip doesn't immediately open as well.
I've tried e.target !== e.currentTarget within an if statement and a number of other things that didn't quite work. My suspicion is that the plugin as it stands is not going to be easily extensible without more than a few simple conditionals, but if anyone can have a look and let me know if possible and if so hopefully offer me some direction or suggestions on how I might go about amending it it would be much appreciated.
If anyone is wondering why I am trying to reinvent the wheel (when there are so many tooltip plugins out there): it's mainly a learning exercise. I'd love to get better at writing reusable code. I'd also like a viable tooltip plugin that suits my needs in regards to being something very lightweight and framework independent, which in my searching I wasn't able to find.
Below is the code as it stands, in one of the two before mentioned states:
(window => {
let template = document.createElement('div'),
pos = {x: 0, y: 0},
targetPos,
tip,
tipWidth,
tipHeight,
innerOffsetX,
delay;
template.inner = document.createElement('div');
template.inner.setAttribute('class', 'tooltip-inner');
template.appendChild(template.inner);
/**
* #param {string|HTMLElement} container Either a selector string or the element.
* #param {Object=} config Optional argument for overriding the default configuration.
*/
class Tooltip {
constructor(container, config) {
this.offsetX = 0;
this.offsetY = 0;
this.position = 'top';
this.margin = 6;
this.offsetBubble = 0;
this.delayShow = this.delayHide = 0;
this.clickToShow = false;
this.tooltipInClass = 'tooltip-in';
this.tooltip = null;
this.toggle = true;
if (typeof container === 'string') {
this.container = document.querySelector(container);
} else {
this.container = container;
}
if (config) {
for (let p in config) {
if (typeof config[p] === 'object') {
this.delayShow = config.delay.show;
this.delayHide = config.delay.hide;
} else {
this[p] = config[p];
}
}
}
template.setAttribute('class', 'tooltip ' + this.position);
}
show() {
return e => {
if (e.target.hasAttribute('data-tooltip')) {
if (this.toggle === true) {
targetPos = e.target.getBoundingClientRect();
pos.x = targetPos.left + e.target.offsetWidth / 2 + this.offsetX;
pos.y = targetPos[this.position] + this.offsetY + document.body.scrollTop;
template.inner.innerText = e.target.getAttribute('data-tooltip');
this.tooltip = document.body.appendChild(template.cloneNode(true));
tip = this.tooltip;
tipWidth = tip.clientWidth;
tipHeight = tip.clientHeight;
pos.x -= tipWidth / 2;
pos.y -= this.position === 'bottom' ? -8 : tipHeight;
// Nudge tooltip content into the window area if needed
if (pos.x + tipWidth > tip.offsetParent.clientWidth) {
innerOffsetX = pos.x + tipWidth - tip.offsetParent.clientWidth + 6;
tip.firstChild.setAttribute('style', `left:-${innerOffsetX}px`);
} else if (pos.x < 0) {
innerOffsetX = -pos.x + 6;
tip.firstChild.setAttribute('style', `left:${innerOffsetX}px`);
}
// Reposition tooltip below/above target and flip arrow if needed
if (pos.y < 0 && this.position !== 'bottom') {
pos.y += tipHeight * 2;
tip.classList.remove(this.position);
tip.classList.add('bottom');
} else if (pos.y + tipHeight > tip.offsetParent.scrollHeight && self.position !== 'top') {
tip.classList.remove(this.position);
tip.classList.add('top');
}
tip.setAttribute('style', `left:${Math.floor(pos.x)}px;top:${Math.floor(pos.y)}px`);
if (this.delayShow !== 0) {
// Don't delay showing the tooltip if entering an adjacent item with a "tooltip" data attribute.
if (e.relatedTarget.hasAttribute('data-tooltip')) {
delay = 0;
} else {
delay = this.delayShow;
}
if (typeof this.timeoutid === 'number') {
clearTimeout(this.timeoutid);
delete this.timeoutid;
}
this.timeoutid = setTimeout(function () {
tip.classList.add(this.tooltipInClass);
this.toggle = false;
}, delay);
} else {
tip.classList.add(this.tooltipInClass);
this.toggle = false;
}
} else {
this.hide('hide');
this.toggle = true;
}
}
};
}
hide(e) {
if (e.target && e.target.hasAttribute('data-tooltip') || typeof e === 'string') {
document.body.lastChild.classList.remove(this.tooltipInClass);
document.body.removeChild(document.body.lastChild);
}
}
init() {
if (!this.clickToShow) {
this.container.addEventListener('mouseover', this.show());
this.container.addEventListener('mouseout', this.hide);
} else {
this.container.addEventListener('click', this.show());
}
}
}
window.Tooltip = window.Tooltip || Tooltip;
})(window);
// Usage examples
const elem = document.querySelector('.container'),
tooltip = new Tooltip(elem, {
offsetX: -2,
delay: {show: 0, hide: 0},
clickToShow: true
}).init();
And a jsfiddle: http://jsfiddle.net/damo_s/et9hLnkt/
Again, any help would be much appreciated.
I changed a little your plugin, I added two thing to handle the problem:
I added this if to control if the link you clicked is the same target if some tooltip is already opened:
if (document.getElementsByClassName("tooltip-in")[0] &&
e.target !== document.getElementsByClassName("current-target-tooltip")[0]) {
this.hide('hide');
this.toggle = true;
document.getElementsByClassName("current-target-tooltip")[0].classList.remove("current-target-tooltip");
}
then I just added the add/remove class on the current target in if/else:
if (this.toggle === true) {
e.target.classList.add('current-target-tooltip');
...
} else {
e.target.classList.remove('current-target-tooltip');
this.hide('hide');
this.toggle = true;
}
and here is your updated Fiddle: http://jsfiddle.net/et9hLnkt/18/
I have installed the HotThemes Carousel module in joomla and this is working fine so far. (unfortunately on localhost so can't share link)
The problem I am trying to fix is that for some reason when I click the next button it brings the next 4 images (like its set to move by 4 images or something) and as I only have 6 images in total, it shows up with blank spaces after the last 2 images.
What I would like is if the next button is pressed then this should bring the next image (so 1 at a time) and then just stop on the last image and then the prev button could be used to go the other way.
How can I do this?
/* jQuery Carousel 0.9.1
Copyright 2008-2009 Thomas Lanciaux and Pierre Bertet.
This software is licensed under the CC-GNU LGPL
<http://creativecommons.org/licenses/LGPL/2.1/>
*/
;(function(jQuery){
jQuery.fn.carousel = function(params){
var params = jQuery.extend({
direction: "horizontal",
loop: false,
dispItems: 5,
pagination: false,
paginationPosition: "inside",
nextBtn: '<span>Next</span>',
prevBtn: '<span>Previous</span>',
btnsPosition: "inside",
nextBtnInsert: "appendTo",
prevBtnInsert: "prependTo",
nextBtnInsertFn: false,
prevBtnInsertFn: false,
autoSlide: false,
autoSlideInterval: 3000,
delayAutoSlide: false,
combinedClasses: false,
effect: "slide",
slideEasing: "swing",
animSpeed: "normal",
equalWidths: "true",
callback: function(){},
useAddress: false,
adressIdentifier: "carousel"
}, params);
// Buttons position
if (params.btnsPosition == "inside"){
params.prevBtnInsert = "insertBefore";
params.nextBtnInsert = "insertAfter";
}
// Slide delay
params.delayAutoSlide = params.delayAutoSlide || params.autoSlideInterval;
return this.each(function(){
// Env object
var env = {
$elts: {},
params: params,
launchOnLoad: []
};
// Carousel main container
env.$elts.carousel = jQuery(this).addClass("js");
// Carousel content
env.$elts.content = jQuery(this).children().css({position: "absolute", "top": 0});
// Content wrapper
env.$elts.wrap = env.$elts.content.wrap('<div class="carousel-wrap"></div>').parent().css({overflow: "hidden", position: "relative"});
// env.steps object
env.steps = {
first: 0, // First step
count: env.$elts.content.children().length // Items count
};
// Last visible step
env.steps.last = env.steps.count - 1;
// Prev Button
if (jQuery.isFunction(env.params.prevBtnInsertFn)) {
env.$elts.prevBtn = env.params.prevBtnInsertFn(env.$elts);
} else {
env.$elts.prevBtn = jQuery(params.prevBtn)[params.prevBtnInsert](env.$elts.carousel);
}
// Next Button
if (jQuery.isFunction(env.params.nextBtnInsertFn)) {
env.$elts.nextBtn = env.params.nextBtnInsertFn(env.$elts);
} else {
env.$elts.nextBtn = jQuery(params.nextBtn)[params.nextBtnInsert](env.$elts.carousel);
}
// Add buttons classes / data
env.$elts.nextBtn.addClass("carousel-control next carousel-next");
env.$elts.prevBtn.addClass("carousel-control previous carousel-previous");
// Bind events on next / prev buttons
initButtonsEvents(env);
// Pagination
if (env.params.pagination) {
initPagination(env);
}
// Address plugin
initAddress(env);
// On document load...
jQuery(function(){
// First item
var $firstItem = env.$elts.content.children(":first");
// Width 1/3 : Get default item width
env.itemWidth = $firstItem.outerWidth();
// Width 2/3 : Define content width
if (params.direction == "vertical"){
env.contentWidth = env.itemWidth;
} else {
if (params.equalWidths) {
env.contentWidth = env.itemWidth * env.steps.count;
} else {
env.contentWidth = (function(){
var totalWidth = 0;
env.$elts.content.children().each(function(){
totalWidth += jQuery(this).outerWidth();
});
return totalWidth;
})();
}
}
// Width 3/3 : Set content width to container
env.$elts.content.width( env.contentWidth );
// Height 1/2 : Get default item height
env.itemHeight = $firstItem.outerHeight();
// Height 2/2 : Set content height to container
if (params.direction == "vertical"){
env.$elts.content.css({height:env.itemHeight * env.steps.count + "px"});
env.$elts.content.parent().css({height:env.itemHeight * env.params.dispItems + "px"});
} else {
env.$elts.content.parent().css({height:env.itemHeight + "px"});
}
// Update Next / Prev buttons state
updateButtonsState(env);
// Launch function added to "document ready" event
jQuery.each(env.launchOnLoad, function(i,fn){
fn();
});
// Launch autoslide
if (env.params.autoSlide){
window.setTimeout(function(){
env.autoSlideInterval = window.setInterval(function(){
goToStep( env, getRelativeStep(env, "next") );
}, env.params.autoSlideInterval);
}, env.params.delayAutoSlide);
}
});
});
};
// Next / Prev buttons events only
function initButtonsEvents(env){
env.$elts.nextBtn.add(env.$elts.prevBtn)
.bind("enable", function(){
var $this = jQuery(this)
.unbind("click")
.bind("click", function(){
goToStep( env, getRelativeStep(env, ($this.is(".next")? "next" : "prev" )) );
stopAutoSlide(env);
})
.removeClass("disabled");
// Combined classes (IE6 compatibility)
if (env.params.combinedClasses) {
$this.removeClass("next-disabled previous-disabled");
}
})
.bind("disable", function(){
var $this = jQuery(this).unbind("click").addClass("disabled");
// Combined classes (IE6 compatibility)
if (env.params.combinedClasses) {
if ($this.is(".next")) {
$this.addClass("next-disabled");
} else if ($this.is(".previous")) {
$this.addClass("previous-disabled");
}
}
})
.hover(function(){
jQuery(this).toggleClass("hover");
});
};
// Pagination
function initPagination(env){
env.$elts.pagination = jQuery('<div class="center-wrap"><div class="carousel-pagination"><p></p></div></div>')[((env.params.paginationPosition == "outside")? "insertAfter" : "appendTo")](env.$elts.carousel).find("p");
env.$elts.paginationBtns = jQuery([]);
env.$elts.content.find("li").each(function(i){
if (i % env.params.dispItems == 0) {
env.$elts.paginationBtns = env.$elts.paginationBtns.add( jQuery('<a role="button"><span>'+( env.$elts.paginationBtns.length + 1 )+'</span></a>').data("firstStep", i) );
}
});
env.$elts.paginationBtns.appendTo(env.$elts.pagination);
env.$elts.paginationBtns.slice(0,1).addClass("active");
// Events
env.launchOnLoad.push(function(){
env.$elts.paginationBtns.click(function(e){
goToStep( env, jQuery(this).data("firstStep") );
stopAutoSlide(env);
});
});
};
// Address plugin
function initAddress(env) {
if (env.params.useAddress && jQuery.isFunction(jQuery.fn.address)) {
jQuery.address
.init(function(e) {
var pathNames = jQuery.address.pathNames();
if (pathNames[0] === env.params.adressIdentifier && !!pathNames[1]) {
goToStep(env, pathNames[1]-1);
} else {
jQuery.address.value('/'+ env.params.adressIdentifier +'/1');
}
})
.change(function(e) {
var pathNames = jQuery.address.pathNames();
if (pathNames[0] === env.params.adressIdentifier && !!pathNames[1]) {
goToStep(env, pathNames[1]-1);
}
});
} else {
env.params.useAddress = false;
}
};
function goToStep(env, step) {
// Callback
env.params.callback(step);
// Launch animation
transition(env, step);
// Update first step
env.steps.first = step;
// Update buttons status
updateButtonsState(env);
// Update address (jQuery Address plugin)
if ( env.params.useAddress ) {
jQuery.address.value('/'+ env.params.adressIdentifier +'/' + (step + 1));
}
};
// Get next/prev step, useful for autoSlide
function getRelativeStep(env, position) {
if (position == "prev") {
if ( (env.steps.first - env.params.dispItems) >= 0 ) {
return env.steps.first - env.params.dispItems;
} else {
return ( (env.params.loop)? (env.steps.count - env.params.dispItems) : false );
}
} else if (position == "next") {
if ( (env.steps.first + env.params.dispItems) < env.steps.count ) {
return env.steps.first + env.params.dispItems;
} else {
return ( (env.params.loop)? 0 : false );
}
}
};
// Animation
function transition(env, step) {
// Effect
switch (env.params.effect){
// No effect
case "no":
if (env.params.direction == "vertical"){
env.$elts.content.css("top", -(env.itemHeight * step) + "px");
} else {
env.$elts.content.css("left", -(env.itemWidth * step) + "px");
}
break;
// Fade effect
case "fade":
if (env.params.direction == "vertical"){
env.$elts.content.hide().css("top", -(env.itemHeight * step) + "px").fadeIn(env.params.animSpeed);
} else {
env.$elts.content.hide().css("left", -(env.itemWidth * step) + "px").fadeIn(1000);
}
break;
// Slide effect
default:
if (env.params.direction == "vertical"){
env.$elts.content.stop().animate({
top : -(env.itemHeight * step) + "px"
}, env.params.animSpeed, env.params.slideEasing);
} else {
env.$elts.content.stop().animate({
left : -(env.itemWidth * step) + "px"
}, env.params.animSpeed, env.params.slideEasing);
}
break;
}
};
// Update all buttons state : disabled or not
function updateButtonsState(env){
if (getRelativeStep(env, "prev") !== false) {
env.$elts.prevBtn.trigger("enable");
} else {
env.$elts.prevBtn.trigger("disable");
}
if (getRelativeStep(env, "next") !== false) {
env.$elts.nextBtn.trigger("enable");
} else {
env.$elts.nextBtn.trigger("disable");
}
if (env.params.pagination){
env.$elts.paginationBtns.removeClass("active")
.filter(function(){ return (jQuery(this).data("firstStep") == env.steps.first) }).addClass("active");
}
};
// Stop autoslide
function stopAutoSlide(env) {
if (!!env.autoSlideInterval){
window.clearInterval(env.autoSlideInterval);
}
};
})(jQuery);
I used a theme that I changed to show sub menu horizontally, now I want to show sub menu onClick and not on hover, I'm not good at javascript but I think that this code is about the navigation menu:
var nav = {
init: function() {
// Add parent class to items with sub-menus
jQuery("ul.sub-menu").parent().addClass('parent');
var menuTop = 40;
var menuTopReset = 80;
// Enable hover dropdowns for window size above tablet width
jQuery("nav").find(".menu li.parent").hoverIntent({
over: function() {
if (jQuery('#container').width() > 767 || jQuery('body').hasClass('responsive-fixed')) {
// Setup menuLeft variable, with main menu value
var subMenuWidth = jQuery(this).find('ul.sub-menu').first().outerWidth(true);
var mainMenuItemWidth = jQuery(this).outerWidth(true);
var menuLeft = '-' + (Math.round(subMenuWidth / 2) - Math.round(mainMenuItemWidth / 2)) + 'px';
var menuContainer = jQuery(this).parent().parent();
// Check if this is the top bar menu
if (menuContainer.hasClass("top-menu")) {
if (menuContainer.parent().parent().parent().hasClass("top-bar-menu-right")) {
menuLeft = "";
} else {
menuLeft = "-1px";
}
menuTop = 30;
menuTopReset = 40;
} else if (menuContainer.hasClass("header-menu")) {
menuLeft = "-1px";
menuTop = 28;
menuTopReset = 40;
} else if (menuContainer.hasClass("mini-menu") || menuContainer.parent().hasClass("mini-menu")) {
menuTop = 40;
menuTopReset = 58;
} else {
menuTop = 44;
menuTopReset = 64;
}
// Check if second level dropdown
if (jQuery(this).find('ul.sub-menu').first().parent().parent().hasClass("sub-menu")) {
menuLeft = jQuery(this).find('ul.sub-menu').first().parent().parent().outerWidth(true) - 2;
}
jQuery(this).find('ul.sub-menu').first().addClass('show-dropdown').css('top', menuTop);
}
},
out:function() {
if (jQuery('#container').width() > 767 || jQuery('body').hasClass('responsive-fixed')) {
jQuery(this).find('ul.sub-menu').first().removeClass('show-dropdown').css('top', menuTopReset);
}
}
});
jQuery(".shopping-bag-item").live("mouseenter", function() {
var subMenuTop = 44;
if (jQuery(this).parent().parent().hasClass("mini-menu")) {
subMenuTop = 40;
}
jQuery(this).find('ul.sub-menu').first().addClass('show-dropdown').css('top', subMenuTop);
}).live("mouseleave", function() {
if (jQuery('#container').width() > 767 || jQuery('body').hasClass('responsive-fixed')) {
jQuery(this).find('ul.sub-menu').first().removeClass('show-dropdown').css('top', 64);
}
});
// Toggle Mobile Nav show/hide
jQuery('a.show-main-nav').on('click', function(e) {
e.preventDefault();
if (jQuery('#main-navigation').is(':visible')) {
jQuery('.header-overlay .header-wrap').css('position', '');
} else {
jQuery('.header-overlay .header-wrap').css('position', 'relative');
}
jQuery('#main-navigation').toggle();
});
jQuery(window).smartresize(function(){
if (jQuery('#container').width() > 767 || jQuery('body').hasClass('responsive-fixed')) {
var menus = jQuery('nav').find('ul.menu');
menus.each(function() {
jQuery(this).css("display", "");
});
}
});
// Set current language to top bar item
var currentLanguage = jQuery('li.aux-languages').find('.current-language span').text();
if (currentLanguage !== "") {
jQuery('li.aux-languages > a').text(currentLanguage);
}
},
hideNav: function(subnav) {
setTimeout(function() {
if (subnav.css("opacity") === "0") {
subnav.css("display", "none");
}
}, 300);
}
};
I tried to replace "hoverIntent" by "click" but it doesn't work, what can I do?
What's happening when someone currently hovers, it does one thing while hovering and when they leave it has to d a sort of cleanup which are the two functions within hoverintent, namely over and out so the code would need to be split into two event listeners one for click of the element and one for blur
I have chained the two listeners to the inital selector so it should all work.
var nav = {
init: function() {
// Add parent class to items with sub-menus
jQuery("ul.sub-menu").parent().addClass('parent');
var menuTop = 40;
var menuTopReset = 80;
// Enable click dropdowns for window size above tablet width
jQuery("nav").find(".menu li.parent").on('click', function (event) {
if($(event.target).parent().hasClass('menu-item-has-children')){
event.preventDefault();
};
if (jQuery('#container').width() > 767 || jQuery('body').hasClass('responsive-fixed')) {
// Setup menuLeft variable, with main menu value
var subMenuWidth = jQuery(this).find('ul.sub-menu').first().outerWidth(true);
var mainMenuItemWidth = jQuery(this).outerWidth(true);
var menuLeft = '-' + (Math.round(subMenuWidth / 2) - Math.round(mainMenuItemWidth / 2)) + 'px';
var menuContainer = jQuery(this).parent().parent();
// Check if this is the top bar menu
if (menuContainer.hasClass("top-menu")) {
if (menuContainer.parent().parent().parent().hasClass("top-bar-menu-right")) {
menuLeft = "";
} else {
menuLeft = "-1px";
}
menuTop = 30;
menuTopReset = 40;
} else if (menuContainer.hasClass("header-menu")) {
menuLeft = "-1px";
menuTop = 28;
menuTopReset = 40;
} else if (menuContainer.hasClass("mini-menu") || menuContainer.parent().hasClass("mini-menu")) {
menuTop = 40;
menuTopReset = 58;
} else {
menuTop = 44;
menuTopReset = 64;
}
// Check if second level dropdown
if (jQuery(this).find('ul.sub-menu').first().parent().parent().hasClass("sub-menu")) {
menuLeft = jQuery(this).find('ul.sub-menu').first().parent().parent().outerWidth(true) - 2;
}
jQuery(this).find('ul.sub-menu').first().addClass('show-dropdown').css('top', menuTop);
}
}).on('mouseleave',function () {
if (jQuery('#container').width() > 767 || jQuery('body').hasClass('responsive-fixed')) {
jQuery(this).find('ul.sub-menu').first().removeClass('show-dropdown').css('top', menuTopReset);
}
});
// Toggle Mobile Nav show/hide
jQuery('a.show-main-nav').on('click', function(e) {
e.preventDefault();
if (jQuery('#main-navigation').is(':visible')) {
jQuery('.header-overlay .header-wrap').css('position', '');
} else {
jQuery('.header-overlay .header-wrap').css('position', 'relative');
}
jQuery('#main-navigation').toggle();
});
jQuery(window).smartresize(function(){
if (jQuery('#container').width() > 767 || jQuery('body').hasClass('responsive-fixed')) {
var menus = jQuery('nav').find('ul.menu');
menus.each(function() {
jQuery(this).css("display", "");
});
}
});
// Set current language to top bar item
var currentLanguage = jQuery('li.aux-languages').find('.current-language span').text();
if (currentLanguage !== "") {
jQuery('li.aux-languages > a').text(currentLanguage);
}
},
hideNav: function(subnav) {
setTimeout(function() {
if (subnav.css("opacity") === "0") {
subnav.css("display", "none");
}
}, 300);
}
};
That seems overly complicated. Do you have a link to the live version?
Usually, when doing a click-to-see submenu with a clickable parent, I set a variable to see whether the menu is open, if not dont go to link. if so, go to link.
An example: http://codepen.io/jhealey5/pen/iLgom
var $handle = $('.sub-menu').prev();
var opened = 0;
$handle.on('click', function(e){
if (opened) {
window.location.href = $(this).attr('href');
} else {
e.preventDefault();
e.stopPropagation();
$('.sub-menu').slideToggle();
opened = 1;
}
});
$('html').on('click', function(){
if (opened) {
$('.sub-menu').slideToggle();
opened = 0;
}
});
Depending on that menu of yours, you could use something similar. But it's using a lot of code for a menu.