Jquery Tooltip plugin, don't repeat div append? - javascript

Ok, I can't see to get this.
I'm making a tooltip plugin that can be applied to multiple items.
I have multiple anchors on a page when mousedover they act as tooltips. I use the variable c_ttip to act as an identifier for the div because I don't want to set an ID on it.
I want to keep from appending the tooltip and applying the class and css over and over if the same anchor is remoused over before the tooltip mouseout activates the timeout that removes it.
script dev page, http://kalluna.com/_dev-js.do
var c_ttip = $('<div>');
return this.each(function() {
$(this).mouseover(function() {
c_ttip.appendTo(document.body).hide();
c_ttip.html('inside my tooltip').addClass('c_ttip_box').css({'top' : obj.position().top + 20, 'left' : obj.position().left}).show();
}).mouseout(function() {
timer = setTimeout(function() {
c_ttip.fadeOut(200, function() { c_ttip.remove();});
}, 2000);
});
});

You can store the object that triggered the tooltip and verify that the object is different before adding the div. Something like:
var c_ttip = $('<div>');
var currobject;
...
$(this).mouseover(function() {
if(this == currobject) return;
currobject = this;
...

You need some form of mutex/flag/semaphore to indicate that you are currently in a timeout/fade situation and that the mouseover() event should not fire.
The timer variable you created for the timeout ID could be used for this purpose. Create it above as a variable (initially false), like you have for c_ttip. Then set it to false in the fadeOut() callback. The mouseover() routine should check to see if timer != false when it starts, and exit straight away if it is.
Alternatively, you can add a class to the tooltip when the timer is active, and remove it when the fade has completed.

Related

Multiple tippy.js instances on a single html element with different trigger options

I am trying to do something very similar (if not the same) to what's been asked in the question here (the answer didn't work for me, it seems like the answerer/creator didn't understand the question).
My goal is to have two tippy tooltip instances on a single html element with different trigger options:
one which will be triggered on mouseenter event (the default one, created using default tippy constructor without using the trigger option at all), and
one which will be triggered on click event (using trigger option manual and calling the tippy show() function afterwards).
This is how I did it:
var myelement = document.getElementById('myelementid');
// Default way of creating tippy tooltips
tippy(myelement, {
content: 'Shown on hover.'
});
// Creating a tooltip which will be triggered manually/programmatically
var mytippy = tippy(myelement, {
content: 'Shown on click.',
trigger: 'manual'
});
myelement.addEventListener("click", function() {
mytippy.show(300);
setTimeout(function(){ mytippy.hide(300); }, 1500);
});
And for some reason it won't show the manually triggered tooltip on that element at all. I get this exception: Uncaught TypeError: Cannot read property 'show' of undefined at HTMLImageElement.<anonymous> (refers to the tippy show() function). But when I delete one of them (tippy instances), the other one works perfectly.
It looks like Tippy.js uses an HTML attribute on elements for the tooltip (i.e., title or data-tippy). Since duplicate attributes would be invalid markup, a workaround might be to change the tooltip text when the element is clicked. Then, after the user moves away from the element, you could change the tooltip text back.
For example:
let myelement = document.getElementById('myelementid');
let to;
let text = "Show on hover."
let tip = tippy(myelement, {
content: text
});
myelement.addEventListener("click", handleClick);
myelement.addEventListener("mouseout", moveOut);
function moveOut () {
// when the user moves their mouse away from the button
// cancel showing the alternate tooltip text
clearTimeout(to);
// slight delay to prevent "flash" of original tooltip text
setTimeout(function () {
// set the tooltip text back to the original
tip.setContent(text);
}, 200);
}
function handleClick () {
tip.setContent("Click");
tip.show(300);
to = setTimeout(function() {
tip.hide(300);
}, 1500);
}
Here's a fiddle demonstrating this: https://jsfiddle.net/g6odqukr/
In the meantime I've came to an idea to put one tippy tooltip on the element myelement itself and another one on its parentNode element, and at the moment it appears to be the most simple solution (to understand and to write). It is simple as writing two totaly different tooltips. It requires that the parentNode element has the same size as the myelement itself for it to look like the tooltip actually belongs to the same element.
Here the code:
var myelement = document.getElementById('myelementid');
// Default way of creating tippy tooltips
tippy(myelement, {
content: 'Shown on hover.'
});
// Creating a tooltip which will be triggered on click
tippy(myelement.parentNode, {
content: 'Shown on click.',
trigger: 'click'
});
Here a little bit more advanced version: https://jsfiddle.net/zbhf48gn/

jquery: name timeout of anchor dynamic using its id

Hej!
I might be a blockhead here, but I can't figure this out;
I'm building a horizontal menu with the basic hover() function in javascript. When you leave an anchor, there's a short timout before the hover effect disappears.
So what I want to do: Name the Timeout after the ID of the anchor, where the mouseleave is triggered in order to stop the right Timeout again on mouseenter.
On Mouseenter i check which anchor is hovered by getting the id, i cancel the timeout for this id and i add the hvr-Class to the hovered anchor:
var t = $(this).attr('id');
clearTimeout(t);
$(this).addClass('hvr');
and on Mouseleave I want to do basically this:
$(this).attr('id') = setTimeout('doSoemthing()',66);
But that causes an error, because the expression isn't allowed on the left side of the =. I thought about something like this:
var ts = $(this).attr('id');
ts = setTimeout('doSoemthing()',66);
but that doesn't work either, because it just overwrites the variable t. Instead I want to kind of 'print' the value of the variable for the Timeout-Name. If i used fixed Timeout-Names, everything works like a charm, but than there's only one (the same) timeout for every link, which doesn't do it.
Is there a way to do this? Or am I off the track?
That's the complete function:
$("nav a").hover(
function(){
// On MouseEnter
var t = $(this).attr('id');
clearTimeout(t);
$(this).addClass('hvr');
},function(){
// On MouseLeave
$(this).attr('id') = setTimeout(function(){doSomethingHere},66);
}
);
Thanks in advance, Best Regards.
From what I understood, you need to keep a reference to the timeout event based on the hovered element, you can use .data() to do that
I think what you are looking for is
$("nav a").hover(function () {
clearTimeout($(this).data('mltimer'));
$(this).addClass('hvr');
}, function () {
var timer = setTimeout('doSoemthing()', 66);
$(this).data('mltimer', timer)
});

JavaScript / jQuery not on element

How can i create an action for an element which will change when the user is not over it. mouseover and mouseleave can't be applied in this situation because the element is activated on page load and the mouse location can be outside the element.
Here is the condition:
if mouse is not over element:
close element
else:
do nothing
So what i want to know is how can i check with jQuery/JavaScript if the current mouse position is not on the specific element.
Thanks!!
You could bind the event handler to the body and check for the requested target:
$().ready(function() {
$("body").on('mouseover',function(event) {
if($(event.target).attr('id') === 'yourid' ) {
console.log('do close this element');
} else {
console.log('do nothing');
}
});
});
I would use a global boolean to keep track of whether the mouse is currently hovering over the element or not. Then, you can periodically check that boolean using setInterval (with some reasonable time interval) which will decide whether the element should be hidden or not.
var mouseIsOver = false;
$('#elementId').hover(
function () {
mouseIsOver = true;
},
function () {
mouseIsOver = false;
}
);
setInterval(function() {
if (mouseIsOver == false) {
$('#elementId').fadeOut();
}
}, 250);
There is a brilliant jQuery plugin for just this. It's called jQuery outside events.
Check the homepage here: http://benalman.com/code/projects/jquery-outside-events/docs/files/jquery-ba-outside-events-js.html
The usage is super-simple and straight forward. I'd recommend this with great experience earlier.

Checking for visibility of a toggled element

I have a button which toggles the visibility of a <div> below it and want to modify the text on the button depending on the visibility of said <div>.
Here is a live demo on jsFiddle
If you click on "Saved Data", the first time it works correctly, but the next time you click the text does not change. This in itself is behaviour that I don't understand.
Now, I could use multiple handlers for slideToggle(), however, elsewhere in the code I also set intervals which load data next to "Cookie data:" and "Server data:". I don't want these intervals to do anything if the <div> is not visible so I use something like this:
this.timer_cookiedata = setInterval(function(){
if (!$savedData.is(':visible'))
{
return null;
}
// ..
});
I'm worried these intervals are not going to work properly because of this is(':visible') business. So the question is, why does this happen (else statement is ignored), and what can I do to mitigate this?
Check out the updated fiddle. When you check for visibility right after you call slideToggle, jQuery may not have updated the visibility of the element yet since the animation takes some time to finish. For this exact reason, slideToggle has a callback you can use to perform operations after the animation has finished:
$(function () {
var $savedData = $('#savedData');
$('#btn-savedData')
.click(function () {
var $button = jQuery(this);
//I'm checking the visibility in the callback. Inside the callback,
//I can be sure that the animation has completed and the visibility
//has been updated.
$savedData.slideToggle('fast', function () {
if ($savedData.is(':visible')) {
$button.html('visible');
} else {
$button.html('not visible');
}
});
});
});​

Close popup div if element loses focus

I have the following scenario: On a label's mouseover event, I display a div. The div must stay open in order to make selections within the div. On the label's mouseout event, the div must dissappear. The problem is that when my cursor moves from the label to the div, the label's mouseout event is fired, which closes the div before I can get there. I have a global boolean variable called canClose which I set to true or false depending on the case in which it must be closed or kept open. I have removed the functionality to close the div on the label's mouseout event for this purpose.
Below is some example code.
EDIT
I have found a workaround to my problem, event though Alex has also supplied a workable solution.
I added a mouseleave event on the label as well, with a setTimeout function which will execute in 1.5 seconds. This time will give the user enough time to hover over the open div, which will set canClose to false again.
$("#label").live("mouseover", function () {
FRAMEWORK.RenderPopupCalendar();
});
$("#label").live("mouseout", function () {
setTimeout(function(){
if(canClose){
FRAMEWORK.RemovePopupCalendar();
}
},1500);
});
this.RenderPopupCalendar = function () {
FRAMEWORK.RenderCalendarEvents();
}
};
this.RenderCalendarEvents = function () {
$(".popupCalendar").mouseenter(function () {
canClose = false;
});
$(".popupCalendar").mouseleave(function () {
canClose = true;
FRAMEWORK.RemovePopupCalendar();
});
}
this.RemovePopupCalendar = function () {
if (canClose) {
if ($(".popupCalendar").is(":visible")) {
$(".popupCalendar").remove();
}
}
};
Any help please?
I would wrap the <label> and <div> in a containing <div> then do all you mouse/hide events on that.
Check out this fiddle example - http://jsfiddle.net/6MMW6/1
Give your popupCalendar an explicit ID instead of a class selector, e.g.
<div id="popupCalendar">
Reference it with #popupCalendar instead of .popupCalendar.
Now, remove() is quite drastic as it will completely remove the div from the DOM. If you wish to display the calendar again you should just .hide() it.
But your logic seems a bit overly complex, why not just .show() it on mouseenter and .hide() on mouseout events ?
This will close the entire tab page if the tab page loses focus.
How ever if you target it, it can work for something within the page too, just change the target codes.
JavaScript:
<script type="text/javascript" >
delay=1000 // 1 sec = 1000.
closing=""
function closeme(){
closing=setTimeout("self.close()",delay)
// self means the tab page close when losing focus, but you can change and target it too.
}
<!--// add onBlur="closeme()" onfocus="clearTimeout(closing)" to the opening BODY tag//-->
</script>
HTML:
<body onBlur="closeme()" onfocus="clearTimeout(closing)">

Categories