Creating a simple JQuery tooltip? - javascript

Has anybody here got any experience in creating simple tooptips using JQuery?
I have created a table:
<tr id="mon_Section">
<td id="day_Title">Monday</td>`
<td id="mon_Row" onmousemove="calculate_Time(event)"></td>
</tr>`
the function "calculate_Time" will be called and this gets the X position of the mouse cursor and from the value, uses IF and ELSE-IF statements to calculate the values of other variables i have created:
var mon_Pos;
var hour;
var minute;
var orig;
var myxpos;
function calculate_Time(event) {
myxpos = event.pageX;
myxpos = myxpos-194;
if (myxpos<60) {
orig = myxpos;
minute = myxpos;
hour = 07;
} else if (myxpos>=60 && myxpos<120) {
orig = myxpos;
minute=myxpos-60;
hour=08;
}
}
How do i go about creating a stylized tooltip containing the Hour and Minute variables that i created, I need the tooltip to be updated everytime the X position of the cursor changes. I know that with myxpos variable does change whenever the mouse is moved because i tried it out using an alert(myxpos) and as expected, if the mouse moves, a new alert box popups with a new value. I just cant work out how to place that value in a tooltip?

Simplest answer: Don't redo what has already been done and done well. Both support callbacks that can be used to modify the text being displayed.

Related

I have created a table in HTML by using JavaScript but 2 functions I have connected to two buttons stopped working

I have created a table in HTML with information in it by doing like this:
var tab = document.querySelector("table");
for (var obj of death_row) {
var row = `<tr><td>${obj.first_name}</td>
<td>${obj.last_name}</td>
<td>${obj.age_at_execution}</td>
<td>${obj.weight}</td>
<td>${obj.height}</td></tr>`;
tab.innerHTML += row;
}
I have created two buttons called "metric" and "imperial" and when the user clicks on them, the values in height and weight has to change to metric values (default table is showing imperial values). The code I have written for the metric button looks as so:
//Changes the height and weight values to metric values when clicking on "metric"-button.
document.getElementById("metric").onclick = function() {
var tab = document.querySelector("table");
for (var obj of death_row) {
var row = `<tr><td>${obj.first_name}</td>
<td>${obj.last_name}</td>
<td>${obj.age_at_execution}</td>
<td>${((obj.weight)/2.2046).toFixed(1)}</td>
<td>${(((Number(obj.height[0])*12*2.54) + (Number(obj.height[3])*2.54))/100).toFixed(2)}</td></tr>`;
tab.innerHTML += row;
}
};
//Changes the values back to imperial values when user clicks on the 'imperial' button
document.getElementById("imperial").onclick = function() {
var tab = document.querySelector("table");
for (var obj of death_row) {
var row = `<tr><td>${obj.first_name}</td>
<td>${obj.last_name}</td>
<td>${obj.age_at_execution}</td>
<td>${obj.weight}</td>
<td>${obj.height}</td></tr>`;
tab.innerHTML += row;
}
};
The code when clicking the imperial-button is the same as the first piece of code I posted above as the default values is imperial. The problem is that they wont work together. They work individually if I out-comment the others and I can't really seem to be able to identify the problem, so I was hoping one of you would be able to :) Also, when I Add these adjustments to the obj.height:
${(((Number(obj.height[0])*12*2.54) + (Number(obj.height[3])*2.54))/100).toFixed(2)}
I seem to lose a lot of data in the table, which I find very weird as I lose no data by adding the "/2.2046).toFixed(1)" adjustment to the weight object.. Maybe some of you have better luck at seeing through my mistakes:)
Thank you very much:)
The codepen helped immensely! I mentioned looking at the console output at one point. That's critically important because it's telling you what went wrong, so make sure you know how to see it. In codepen its at the bottom left as well, and there's a red exclamation mark in the JS showing that an error is present. When I hit the Metric button I see:
TypeError: obj.height is null
That's because some of your data has null for width or height instead of the value you're expecting, so it "crashes" and just stops at whatever row caused the problem. That's why some rows disappeared.
Two fixes I see:
Fix your data so that the values don't have null in them
Make the code more robust so it doesn't crash on invalid data (this is always preferable for any program).
For the second option, you can fix the code like this:
function MetricValues() {
var tab = document.querySelector("table");
tab.innerHTML = "";
for (var obj of death_row) {
var height = obj.height || "0' 0\"";
var row = `<tr><td>${obj.first_name}</td>
<td>${obj.last_name}</td>
<td>${obj.age_at_execution}</td>
<td>${((obj.weight)/2.2046).toFixed(1)}</td>
<td>${(((Number(height[0])*12*2.54) + (Number(height[3])*2.54))/100).toFixed(2)}</td></tr>`;
tab.innerHTML += row;
}
};
Using a temporary variable var height = obj.height || "0' 0\""; which defaults to 0' 0" fixes the crash.
BTW you definitely should read up on functions.

jQuery UI Draggable: Get drag offset of A LOT of dynamically created elements

I'm creating a <table> element in the DOM and using javascript to dynamically append many cells to it. For the sake of explanation let's say I create 10 rows with 10 fields per row. I'm using simple counters to assign unique IDs for the div containers inside of those fields. Easy enough. This is what I get:
<table>
<tr><td><div id="field0"><div id="handle0"></div></div></td></tr>
.....
<tr><td><div id="field99></div id="handle99"></div></div></td></tr>
</table>
Note that the numbers 0-99 are what is dynamically appended to each element ID.
I now want to go ahead and attach the jQueryUI .draggable function to each handle and retrieve the coordinates of each handle relative to each surrounding parent div like so:
for (var counter = 0; counter < 100; counter++) {
var dragHandle = $('#handle' + counter);
var dragField = $('#field' + counter);
dragHandle.draggable({
containment: dragField,
scroll: false,
drag: function () {
var offset = $(this).offset();
var xPos = (offset.left) - $(this).offsetParent().offset().left;
var yPos = (offset.top) - $(this).offsetParent().offset().top;
console.log(xPos);
console.log(yPos); // These add up?!
}
});
}
Now, the functions work, the table gets properly initialized and all of the individual handles in the table are now draggable.
The problem is that the xPos and yPos values that are returned by the above function are not the correct coordinates relative to each field but instead they add up.
I feel like I'm missing something terribly obvious and would really appreciate if someone could help.
Edit: The example above uses console.log for simplification. My original script performs more complex computations in the on drag event. I won't be able to use a class selector to go through all of the elements like someone suggested in the comments because I need to retrieve unique offset and position values for each unique handle ID relative to its unique containment ID.
var xPos=(offset.left)-$(this).position( ).left
var yPos=(offset.top)-$(this).position( ).top
Instead of offsetParent you can modify.
var xPos = (offset.left) - $(this).parent().offset().left;
var yPos = (offset.top) - $(this).parent().offset().top;

Assistance with calls to a function and the placement of the result a specific location based on the statement that called it using jquery

I am not a web developer but am enjoying working with jQuery on a timesheet form i am creating for my employer's specific needs.
I am working on part of the timesheet that takes the Start Time and Finish Time along with the Meal Break start and finish time and calculates the total hours worked. This works well using examples found on stack overflow and modifications to meet my needs. Below is the code
jQuery(document).ready(function(jQ){
function calculate() {
var StartTime = jQ(".form-group .StartTime").val().split(':'),
FinishTime = jQ(".form-group .FinishTime").val().split(':'),
MealStart = jQ(".form-group .MealStart").val().split(':'),
MealFinish = jQ(".form-group .MealFinish").val().split(':');
var StartHour = parseInt(StartTime[0], 10) || 0,
FinishHour = parseInt(FinishTime[0], 10) || 0,
StartMin = parseInt(StartTime[1], 10) || 0,
FinishMin = parseInt(FinishTime[1], 10) || 0,
MealStartHour = parseInt(MealStart[0], 10) || 0,
MealFinishHour = parseInt(MealFinish[0], 10)|| 0,
MealStartMins = parseInt(MealStart[1], 10) || 0,
MealFinishMins = parseInt(MealFinish[1], 10) || 0;
var CalcHours = FinishHour - StartHour,
CalcMins = 0,
MealHours = MealFinishHour - MealStartHour,
MealMins = 0;
if(CalcHours < 0) CalcHours = 24 + CalcHours;
if(FinishMin >= StartMin) {
CalcMins = FinishMin - StartMin;
}
else {
CalcMins = (FinishMin + 60) - StartMin;
CalcHours--;
}
CalcMins = CalcMins / 60;
CalcHours += CalcMins;
CalcHours = CalcHours.toFixed(2);
if(MealHours < 0) MealHours = 24 + MealHours;
if(MealFinishMins >= MealStartMins) {
MealMins = MealFinishMins - MealStartMins;
}
else {
MealMins = (MealFinishMins + 60) - MealStartMins;
MealHours--;
}
MealMins = MealMins / 60;
MealHours += MealMins;
MealHours = MealHours.toFixed(2);
GrandTotal = CalcHours - MealHours;
//Convert to fixed decimal again
GrandTotal = GrandTotal.toFixed(2);
//Display the total in the correct location on the page
jQ(".Hours").val(GrandTotal);
}
})
i wrote the code as a function as there are 20 locations on the form that the value of the variable Grand_Total will be placed based on the event that triggers the function.e.g an on change event for Wednesday should only update the total field for Wednesday
My first issue is the value assigned to Grand_Total is not available outside the function even though it is not defined as variable, i was of the opinion that if i omitted the var in front of
GrandTotal = CalcHours - MealHours;
that this would make it global and available anywhere. I am assuming the variable needs to be global so i can call on it from different events
My second issue is i am trying not to duplicate the code for every change in location where the value needs to be stored so my question is what is the best way to run event actions like the following to place the grand total where its needed without affecting the totals in other input boxes
jQuery(document).ready(function(jQ){
jQ('#Wed1_StartTime','#Wed1_FinishTime','#Wed1_MealStart','#Wed1_MealFinish').change(function(){
jQ('.Hours').val("Grand_Total");
// i want to place the value in the input box with a class of .Hours for Wed 1 fieldset only
jQuery(document).ready(function(jQ){
jQ('#Thur1_StartTime','#Thur1_FinishTime','#Thur1_MealStart','#Thur1_MealFinish').change(function(){
jQ('.Hours').val("Grand_Total");
// i want to place the value in the input box with a class of .Hours for Thurq fieldset only
//etc
As you can see this will not work as the same value will be input into all input boxes regardless of the fieldset being modified as all total fields have the same class name. Using ID's i feel is bad coding as i would have write 20 versions of the function and call each one individually based on the elements being changed.
Hope this makes sense
Thanks in advance for any help offered
Pointing out couple of issues from your code and assumptions, Firstly
My first issue is the value assigned to Grand_Total is not available outside the function even though it is not defined as variable, i was of the opinion that if i omitted the var in front of that this would make it global and available anywhere.
NO, this assumption is wrong, The variable will be global only when placed in global scope. Explaining your code.
jQuery(document).ready(function(jQ){ //<-- note this block
function calculate() {
// your other code
GrandTotal = CalcHours - MealHours; // you assume this must be global varialbe only because there is no var in front of it.
}
})
The variable scope is within the block, And as you assume it should be global, Yes it is global But global within in block. Its available to all the functions and code with in the block only.
Now how to create a global variable.
1) Placing the variable declaration under script block and not under any function block. hence available for all the scripts.
<script>
var GrandTotal; //<-- this is global, as its placed directly under script tag and not contained inside any block.
jQuery(document).ready(function(jQ){
function calculate() {
// your other code
GrandTotal = CalcHours - MealHours;
}
})
</script>
2) using window scope while declaring the variable. This will place the variable into the window scope, and is global.
<script>
jQuery(document).ready(function(jQ){
function calculate() {
// your other code
window.GrandTotal = CalcHours - MealHours; // note the window.GrandTotal
}
})
</script>
My second issue is i am trying not to duplicate the code for every change in location where the value needs to be stored so my question is what is the best way to run event actions like the following to place the grand total where its needed without affecting the totals in other input boxes
Since you have not posted any HTML, I will give m best to answer close to your HTML, you can change it to stuff that works for you.
To write a common Jquery you must use class selectors. When ever we write code to work generic way then class is the best choice. So put a class to all your input elements to which you want to bind the change event, Also give a class name to all your div's which holds the input elements of each day. Something like this
<div class="DayTimeWrapper">
<input id="Wed1_StartTime" class="setTime" />
<input id="Wed1_FinishTime" class="setTime" />
<input id="Wed1_MealStart" class="setTime" />
<input id="Wed1_MealFinish" class="setTime" />
<input class="Hours" />
</div>
<div class="DayTimeWrapper">
<input id="Thur1_StartTime" class="setTime" />
<input id="Thur1_FinishTime" class="setTime" />
<input id="Thur1_MealStart" class="setTime" />
<input id="Thur1_MealFinish" class="setTime" />
<input class="Hours" />
</div>
Now your Jquery would be as simple as below.
jQ('.setTime').change(function(){ // set event to all the elements at once with class name setTime
jQ(this).closest('.DayTimeWrapper').find('.Hours').val(Grand_Total);
});
jQ(this).closest('.DayTimeWrapper').find('.Hours').val(Grand_Total); : here jQ(this) means the element which triggered the change event. Now find the closest div with class DayTimeWrapper that would be the div which hold this element. Now trace down to find the input with class Hours within this div and set the value.
So this way we can bind events to all the elements with this simple code, Also the hours will be updated for that particular day itself.
let me know if this helps and if you want further help.
EDIT 1: Adding the working fiddle which is the modified version of yours

Type of selections

I am trying to but together a highlighting menu that hovers over a selection with the following properties:
It should appear when a selection is made.
It should disappear when the selection is destroyed.
All that works quite nicely except for one thing: If an existing selection gets clicked the selection disappears and so should the hovering menu. But for whatever reason it doesn't.
When you click outside of an existing selection the selection type changes to 'caret' or to 'none' if you click on that very selection. So I tried setting the visibility of the menu according to the type. The problem is though that although the type of the selection appears to change in the object you get by window.getSelection(), it does not if you try to get the type from the object.
I put this jsfiddle together to illustrate the problem. https://jsfiddle.net/nxo2d7ew/1/
var el = document.getElementById("simple-text");
el.addEventListener("mouseup", placeDiv, false);
function placeDiv(x_pos, y_pos) {
var sel = window.getSelection();
var position = sel.getRangeAt(0).getBoundingClientRect();
// console.log(sel)
// console.log(position)
var highlighter = document.getElementById('highlighter').offsetWidth
var d = document.getElementById('highlighter');
d.style.left = position.left+position.width*0.5-highlighter*0.5 +'px';
d.style.top = position.top-50 +'px';
// console.log('sel.type: ' + sel.type)
var test = window.getSelection()
console.log(test) // returns an object with "type: None"
console.log(test.type) //returns "Range"
if (test.type !== 'Range') {
d.style.visibility = 'hidden';
}
else {
d.style.visibility = 'visible';
}
var sel = ''
}
Thank you :-)
The real change to selection doesn't really happen on mouseup event. There is another step afterwards that changes the selection, so when mouseup is fired, the selection hasn't changed yet. What you see in your console is not the exact state of the selection object on the mouseup event.
I'm not sure there's a cross-browser way to have access to the real selection change event, there's a selectionchange event in Chrome, and supposedly in IE (but I couldn't test it). But in Firefox it's not available. That said, the type property you're using to test if it's an empty selection doesn't seem to work on Firefox either. But you could use isCollapsed.
One way you can maybe solve the problem, though not the most elegant, is using a timeout, you only need a few milliseconds for the selection to update, then your logic will work - using isCollapsed to make it work on Firefox. Like this:
setTimeout(function(){
var test = window.getSelection()
console.log(test) // returns an object with "type: None"
console.log(test.type) //returns "Range"
if (test.isCollapsed) {
d.style.visibility = 'hidden';
}
else {
d.style.visibility = 'visible';
}}, 25);
https://jsfiddle.net/q1f4xfw9/6/
Or with selectionchange event in Chrome, you move the hide condition into this handler. Like this:
document.addEventListener("selectionchange", function () {
var d = document.getElementById('highlighter');
var test = window.getSelection()
if (test.type !== 'Range') {
d.style.visibility = 'hidden';
}
});
https://jsfiddle.net/q1f4xfw9/5/
EDIT:
There's another way to solve the problem, you could remove selection on mouse down using removelAllRanges. Then the selection change event would be triggered before the mouseup. Up to you to see if that little change in functionality works with what you want. Like this:
el.addEventListener("mousedown", function(e){
window.getSelection().removeAllRanges()
}, false);
https://jsfiddle.net/q1f4xfw9/8/

JavaScript, using a function to change the text displayed in a HTML <p> tag

I have a HTML5 canvas, which is displaying a number of images and a paragraph of text on the page underneath the canvas. I want the text in the paragraph to be updated to display a different element from a JS array depending on which image the user clicks on.
Currently, I have a 'mousedown' function that looks like this:
_mousedown: function(evt) {
this._setUserPosition(evt);
var obj = this.getIntersection(this.getUserPosition());
if(obj && obj.shape) {
var shape = obj.shape;
this.clickStart = true;
shape._handleEvent('mousedown', evt);
isClickOnImage(evt);
var id = shape.id;
selectTip(id);
}
//init stage drag and drop
if(Kinetic.DD && this.attrs.draggable) {
this._initDrag();
}
}
I tried using the line var id = shape.id to update the ID that's being passed to the function, so that it will get the correct element from my 'tips' array, but for some reason, when I view the page in the browser, and click on an image, the text beneath the canvas is not updated. It seems that this function is not updating the 'id' variable to the ID of whichever image has been clicked.
After looking into this, it seems to me that I will want to use a loop inside the 'mousedown' function, that will take the 'id' of the image on which the click has been detected, and loop through my 'sources' array (which is where all of the images have been loaded from the HTML into the JS), checking at each position whether the image stored at that location has the same ID as that of the image that has been clicked on. If it does, the loop should set the text to the text stored at that position of the array, and if not, it should continue looking through the array until it find it. Would this make sense? I tried adding the following code to the 'mousedown' function, but it doesn't change the text as I expected:
var imageCheckArray = 0;
while(imageCheckArray < sources.length){
if(shape.id == sources[imageCheckArray]){
selectTip(imageCheckArray);
} else {
imageCheckArray++;
}
}
Is there something I'm missing from the loop?
The code for the whole function currently looks like this:
_mousedown: function(evt) {
this._setUserPosition(evt);
var obj = this.getIntersection(this.getUserPosition());
if(obj && obj.shape) {
var shape = obj.shape;
this.clickStart = true;
shape._handleEvent('mousedown', evt);
isClickOnImage(evt);
/*This line needs to get the element of the sources array that has been selected,
and then select the element at the same position from the tips array.*/
//var id = null;
var imageCheckArray = 0;
while(imageCheckArray < sources.length){
if(shape.id == sources[imageCheckArray]){
selectTip(imageCheckArray);
} else {
imageCheckArray++;
}
}
//var id =
//selectTip(id);
}
//init stage drag and drop
if(Kinetic.DD && this.attrs.draggable) {
this._initDrag();
}
}
Edit 11/01/2013 # 16:10
The code for selectTip is:
function selectTip(id){
$("#tipsParagraph").text(tips[id]);
}
and I've put a jsFiddle up here: http://jsfiddle.net/cd8G7/ although the 'result' panel is not showing what I actually see when I view the page in my browser- I get the canvas with all of the images displayed, and the paragraph underneath the canvas shows the text from the first element of my 'tips' array.
Edit 23/01/2013 # 13:50
Here's my isClickOnImage function:
function isClickOnImage(event){
var clickX = event.clientX;
var clickY = event.clientY;
//var imageCheckIteration = 0;
while(imageCheckIteration < sources.length){
if((clickX > sources[imageCheckIteration].x && clickX < sources[imageCheckIteration].x + imageWidth) &&
(clickY > sources[imageCheckIteration].y && clickY < sources[imageCheckIteration].y + imageHeight)){
/*This is where I need to print the variable that holds the text I want to display, but I need to display its contents
outside the canvas, in the <p></p> tags below. */
console.log("Click on image detected");
document.getElementById("tipsParagraph").innerHTML = sources[imageCheckIteration].data-tip /*tips[imageCheckIteration]*/;
} else {
document.getElementById("tipsParagraph").innerHTML = "";
}
}
}
What I intended that this function do is, capture the X & Y coordinates of any click on the canvas, and store them in the variables "clickX" and "clickY". Then, I have a variable called "imageCheckIteration" that has been initialised to 0, and while this variable is less than the length of my "sources" array (which is the array where all of the images have been stored), the function should check whether the click was on an area of the canvas that is covered by one of the images in the array.
If it was, then a console log should display the message "click on image detected", and the line
document.getElementById("tipsParagraph").innerHTML = sources[imageCheckIteration].data-tip;
should set the value of the "tipsParagraph" to be the value of the 'data-tip' attribute of whichever image is at the 'imageCheckIteration' position of the 'sources' array. If the click was detected on an area of the canvas that does not have an image displayed, then the value of the "tipsParagraph" should be set to hold nothing.
However, for some reason, when I view the page in the browser, the 'tipsParagraph' displays the text "This is where the text will be displayed", which is its default value, so that's fine. But, when I click on an image, or click anywhere else on the canvas, the text displayed in the 'tipsParagraph' is not updated.
I can't figure out why this is- can someone point me in the right direction? Does it mean that my isClickOnImage(event) function is never being called?
I simplified the way you are getting a reference to an image through the canvas. The trick here is to swap the z-index of the canvas and the image container and grab the reference to the image on the mouse up event. I don't know of a clean way to get elements behind a canvas, hence the workaround.
$('canvas').bind('mousedown', function(e) {
$('section').css('z-index', 4);
});
$('img').bind('mouseup', function(e) {
$('#tipsParagraph').text($(this).attr('id') + ":" + $(this).attr('alt'));
$('section').css('z-index', 2);
});
The second portion here is grabbing some attributes from the image itself and updating the text inside your div.
You can see more of the solution here.

Categories