I'm creating a BMI calculator.
This is my code so far:
var $ = function(id) {
return document.getElementById(id);
}
var calculateBmi = function() {
var weight = parseInt($("weight").value);
var height = parseInt($("height").value);
if (weight > 0 && height > 0 && weight !=isNaN && height !=isNaN){
var bmi = Math.floor((weight / (height * height)) * 703);
$("bmi_message").innerHTML = "" + bmi;
}
if (bmi > 18.5) {
$("bmi_message").innerHTML = ": Underweight";
}
else if (bmi >= 18.5 && bmi <= 24.9) {
$("bmi_message").innerHTML = ": Normal";
}
else if (bmi >= 25 && bmi <= 29) {
$("bmi_message").innerHTML = ": Overweight";
}
else if (bmi > 30) {
$("bmi_message").innerHTML = ": Obese";
}
else if (isNaN(weight)) {
$("weight_message").innerHTML = "Enter numerical weight value (lbs)";
}
else if (isNaN(height)) {
$("height_message").innerHTML = "Enter numerical height value (kg)";
}
}
window.onload = function () {
$("calculateBmi").onclick = calculateBmi;
}
I have all these conditions (bmi > x OR bmi < x) that require different actions. Each range should result in a different text being displayed in my span element, however this is not happening. Could someone explain to me what is wrong and maybe provide me with better practices?
In your code
if (bmi > 18.5) {
$("bmi_message").innerHTML = ": Underweight";
}
will always be true for any value higher than 18.5 so you wont get any other value but always Underweight.
Try
if (bmi < 18.5) {
$("bmi_message").innerHTML = ": Underweight";
}
This only points out a third particular issue in your code. You are checking if weight (and height) are numbers with weight != isNaN, but:
100 != isNaN // --> true
'text' != isNaN // --> true
isNaN isn't a static value but a function to use like so:
var weight = 100; isNaN(weight) // -->false
!isNaN(weight) // --> true
var height = 'text'; isNaN(height) // --> true
!isNaN(height) // --> false
You should consider all three answers here together to get the right code.
Related
I'm creating a little graphic editor, where the user (by now) could insert some "symbols", that consist of some svg-elements, grouped inside a g-tag.
Additionally, he could draw lines in different colors yet.
By now I am able to select single drawn lines and Symbols and I also could select more objects by clicking on them, while Holding the Control-key. (For those, who are interrested in it, a selected object gets a class "selected", so I could find them programatically by d3.select('.selected').)
My new Goal ist to draw a rectangle with the mouse over such Elements and select the Elements inside the rectangle.
For this, I catch the pointerdown-event, where I add a rectangle to the svg-box and scale it inside pointermove-event.
Attached, a simple Video of my actual Version.
I have two Questions by now:
1) How can I avoid that the Elements are higlited like selected text while moving the mouse with pressed left button? (you can see the flickering in the Video) Is there perhaps something like event.preventDefault(); to do so?
2) ...and that is the greater problem…
Is drawing a rectangle a good way to do this and how can i quickly calculate which elements are inside this rectangle? Is there perhaps a specialized function in d3, that I didn't find yet?
EDIT: for clarification, I attached a screenshot of the svg-structur of a Symbol and a line:
Simple sample video
CodePen example: https://codepen.io/Telefisch/pen/LoEReP
$(document).ready(function () {
svgDrawing = document.getElementById('drawing');
svgDrawing.addEventListener('pointerdown', mouseButtonPressed);
svgDrawing.addEventListener('pointerup', mouseButtonReleased);
svgDrawing.addEventListener('pointermove', mouseMove);
}) ...
Additional question:
What's the difference between svg_children[i].className.baseVal += ' selected'; and svg_children[i].classList.add('selected')I have some problems that baseVal seems not to be stored inside the dom? If I use it that way, I couldn't see the class in the elements-pane of the developer-window, but it pops up at the symbol. If I use ClassList.add, I can see the class in the Elements-Pane also.
Screenshot:
As you can see, the yellow-marked seems to have the class in the popup but not in the Elements-code. This is added by svg_children[i].className.baseVal += ' selected';
The red-marked 'selected'-class was added by svg_children[i].classList.add('selected')
Thanks so far, Carsten
I think I have a solution for you, using .getClientBoundingRect() of the svg elements and the <rect.selectionBox/> to find out if your box is overlapping them etc.
Demo - https://codepen.io/Alexander9111/pen/XWJQoPP:
Code:
var svgDrawing = document.getElementById('drawing');
var pointerOrigin;
var point = svgDrawing.createSVGPoint();
var drawRectToSelect
var raster = 10;
$(document).ready(function () {
svgDrawing = document.getElementById('drawing');
svg_rect = svgDrawing.getBoundingClientRect();
console.log("svg_rect", svg_rect);
g = document.getElementById("437");
//svg_children = g.childNodes;
svg_children = g.querySelectorAll("*");
console.log(svg_children);
svgDrawing.addEventListener('pointerdown', e => mouseButtonPressed(e));
svgDrawing.addEventListener('pointerup', e => mouseButtonReleased(e));
svgDrawing.addEventListener('pointermove', e => mouseMove(e));
})
function mouseButtonPressed(evt) {
pointerOrigin = getPointFromEvent(evt);
if(evt.button === 0)
{
drawRectToSelect = d3.select('#drawing')
.append('rect')
.attr("id","temp_selection")
.classed('selectionBox', true)
.attr("x", Math.round(pointerOrigin.x / raster) * raster)
.attr("y", Math.round(pointerOrigin.y / raster) * raster)
.attr("height", raster)
.attr("width", raster);
}
}
function mouseMove(evt) {
if (!drawRectToSelect) { return; }
evt.preventDefault(); //Verschieben der gesamten Seite unterbinden
var pointerPosition = getPointFromEvent(evt);
if (drawRectToSelect) {
drawRectToSelect
.attr("width", Math.round((pointerPosition.x - pointerOrigin.x) / raster) * raster)
.attr("height", Math.round((pointerPosition.y - pointerOrigin.y) / raster) * raster);
}
}
function elementIsInside(el, box){
var result = false;
el_rect = el.getBoundingClientRect();
box_rect = box.getBoundingClientRect();
// console.log("rects_" + el.tagName, el_rect, box_rect)
// console.log("rects_" + el.tagName, el, box)
if (el_rect.right >= box_rect.left && el_rect.right <= box_rect.right
&& el_rect.bottom >= box_rect.top && el_rect.bottom <= box_rect.bottom){
result = true;
} else if (el_rect.left >= box_rect.left && el_rect.left <= box_rect.right
&& el_rect.bottom >= box_rect.top && el_rect.bottom <= box_rect.bottom){
result = true;
} else if (el_rect.right >= box_rect.left && el_rect.right <= box_rect.right
&& el_rect.top >= box_rect.top && el_rect.top <= box_rect.bottom){
result = true;
} else if (el_rect.left >= box_rect.left && el_rect.left <= box_rect.right
&& el_rect.top >= box_rect.top && el_rect.top <= box_rect.bottom){
result = true;
}
// console.log("result_" + el.tagName, result)
return result;
}
function mouseButtonReleased(evt) {
svgDrawing.style.cursor = null;
if (drawRectToSelect) {
const box = document.querySelector('#temp_selection');
for (i=0; i < svg_children.length; i++){
//svg_children[i].classList.add("selected");
console.log(svg_children[i].tagName)
console.log(svg_children[i].className.baseVal)
child_rect = svg_children[i].getBoundingClientRect();
console.log(child_rect);
//calculate elements inside rectangle
if (elementIsInside(svg_children[i], box )){
if (svg_children[i].className.baseVal.includes('selected')){
} else {
svg_children[i].className.baseVal += " selected";
svg_children[i].className.animVal += " selected";
}
} else {
if (svg_children[i].className.baseVal.includes('selected')){
console.log("true")
svg_children[i].className.baseVal = svg_children[i].className.baseVal.replace(" selected"," ");
svg_children[i].className.animVal = svg_children[i].className.animVal.replace(" selected"," ");
console.log(svg_children[i].className.baseVal);
} else {
console.log("false")
console.log(svg_children[i].className.baseVal);
}
}
}
//Delete selection-rectangle
drawRectToSelect.remove();
drawRectToSelect = null;
}
}
function getPointFromEvent(evt) {
if (evt.targetTouches) {
point.x = evt.targetTouches[0].clientX;
point.y = evt.targetTouches[0].clientY;
} else {
point.x = evt.clientX;
point.y = evt.clientY;
}
var invertedSVGMatrix = svgDrawing.getScreenCTM().inverse();
return point.matrixTransform(invertedSVGMatrix);
}
Firstly, you have to pass in the event argument to use it later:
$(document).ready(function () {
svgDrawing = document.getElementById('drawing');
svg_rect = svgDrawing.getBoundingClientRect();
console.log("svg_rect", svg_rect);
g = document.getElementById("437");
//svg_children = g.childNodes;
svg_children = g.querySelectorAll("*");
console.log(svg_children);
svgDrawing.addEventListener('pointerdown', e => mouseButtonPressed(e));
svgDrawing.addEventListener('pointerup', e => mouseButtonReleased(e));
svgDrawing.addEventListener('pointermove', e => mouseMove(e));
})
Then I created a function which tests if the box overlaps at least 1 corner of the element's bounding box:
function elementIsInside(el, box){
var result = false;
el_rect = el.getBoundingClientRect();
box_rect = box.getBoundingClientRect();
// console.log("rects_" + el.tagName, el_rect, box_rect)
// console.log("rects_" + el.tagName, el, box)
if (el_rect.right >= box_rect.left && el_rect.right <= box_rect.right
&& el_rect.bottom >= box_rect.top && el_rect.bottom <= box_rect.bottom){
result = true;
} else if (el_rect.left >= box_rect.left && el_rect.left <= box_rect.right
&& el_rect.bottom >= box_rect.top && el_rect.bottom <= box_rect.bottom){
result = true;
} else if (el_rect.right >= box_rect.left && el_rect.right <= box_rect.right
&& el_rect.top >= box_rect.top && el_rect.top <= box_rect.bottom){
result = true;
} else if (el_rect.left >= box_rect.left && el_rect.left <= box_rect.right
&& el_rect.top >= box_rect.top && el_rect.top <= box_rect.bottom){
result = true;
}
// console.log("result_" + el.tagName, result)
return result;
}
And this gets called from your function (and adds or removes the .selected class):
function mouseButtonReleased(evt) {
svgDrawing.style.cursor = null;
if (drawRectToSelect) {
const box = document.querySelector('#temp_selection');
for (i=0; i < svg_children.length; i++){
//svg_children[i].classList.add("selected");
console.log(svg_children[i].tagName)
console.log(svg_children[i].className.baseVal)
child_rect = svg_children[i].getBoundingClientRect();
console.log(child_rect);
//calculate elements inside rectangle
if (elementIsInside(svg_children[i], box )){
if (svg_children[i].className.baseVal.includes('selected')){
} else {
svg_children[i].className.baseVal += " selected";
svg_children[i].className.animVal += " selected";
}
} else {
if (svg_children[i].className.baseVal.includes('selected')){
console.log("true")
svg_children[i].className.baseVal = svg_children[i].className.baseVal.replace(" selected"," ");
svg_children[i].className.animVal = svg_children[i].className.animVal.replace(" selected"," ");
console.log(svg_children[i].className.baseVal);
} else {
console.log("false")
console.log(svg_children[i].className.baseVal);
}
}
}
//Delete selection-rectangle
drawRectToSelect.remove();
drawRectToSelect = null;
}
}
I am building a sample application, a mockup of a medical records system, using Oracle Apex. On one page, the object is to enter patient vitals. What is required is that, once the patient's height (feet and inches) and weight are entered, which are three separate textboxes, the BMI is automatically calculated. I attempted to set this using dynamic actions, but the result was that, anytime any one of the textboxes was changed, the calculation would fire. I would like this calculation to only fire if and only if all three textboxes have both been changed and have valid data. Listed below is the current status of the Javascript function:
function getBMI() {
var feet = parseInt(document.getElementById('P601_HEIGHTFT').value);
var inches = parseInt(document.getElementById('P601_HEIGHTIN').value);
var weight = parseInt(document.getElementById('P601_WEIGHT').value);
var height = (feet * 12) + inches;
var warning = document.getElementById('BMIWarning');
while ((feet == '') && (inches == '') && (weight == '')) {
document.getElementById('P601_BMI').value = '';
}
var BMI = (weight / Math.pow(height, 2)) * 703;
document.getElementById('P601_BMI').value = BMI.toFixed(2);
if(BMI < 18.5) {
warning.style.color = "red";
warning.innerHTML = "Patient is underweight";
} else if (BMI >= 18.5 && BMI < 25.0) {
warning.innerHTML = "";
} else if (BMI >= 25.0 && BMI <= 29.9) {
warning.style.color = "red";
warning.innerHTML = "Patient is overweight";
} else if (BMI > 29.9) {
warning.style.color = "red";
warning.innerHTML = "Patient is obese";
}
}
While it would function properly after all values were entered, the fact that the function fired after each individual textbox was highly undesired.
I have a simple screen with 2 text boxes, one to enter Methane data, One ot enter Hydrogen Data, I have written a little JS to divide one by the other. As below.
<script type="application/javascript">
function RRC()
{
var Methane = document.getElementById('MethaneVPM').value;
var Hydrogen = document.getElementById('HydrogenVPM').value;
var RRC1 = Methane / Hydrogen;
var RRR1 = parseFloat(RRC1).toFixed(1);
if (!isNaN(RRR1))
{
document.getElementById('RogerRatio').value = RRR1;
}
}
</script>
This works with an on focus, If I put 62 in Methane and 52 in Hydrogen I get 1.2, which is correct.
However when I add some else if statements to it, it fails.
I've been looking at this for days now, I know I am missing something I just can't work out what.
So below just stops responding.
<script type="application/javascript">
function RRC()
{
var Methane = document.getElementById('MethaneVPM').value;
var Hydrogen = document.getElementById('HydrogenVPM').value;
var RRC1 = Methane / Hydrogen;
var RRR1 = parseFloat(RRC1).toFixed(1);
var RRC1R = 0;
if(RRR1 < 0.1){RRC1R = 5;}
else if(RRR1 >= 0.1 && < 0.9){RRC1R = 0;}
else if(RRR1 >= 1 && < 2.9){RRC1R = 1;}
else if(RRR1 >= 3){RRC1R = 2;}
else {RRC1R = 'Boo';}
if (!isNaN(RRC1R))
{
document.getElementById('RogerRatio').value = RRC1R;
}
}
</script>
Any pointers at this stage would be a huge help.
Thanks in advance
You're missing a value in your if statements:
else if(RRR1 >= 0.1 && < 0.9)
should be
else if(RRR1 >= 0.1 && RRR1 < 0.9)
the same goes for all conditions
see the working code here
You are missing RRR1 in your conditions. I would also suggest to change condition as follows:
<script type="application/javascript">
function RRC()
{
var Methane = document.getElementById('MethaneVPM').value;
var Hydrogen = document.getElementById('HydrogenVPM').value;
var RRC1 = Methane / Hydrogen;
var RRR1 = parseFloat(RRC1).toFixed(1);
var RRC1R = 0;
if(RRR1 < 0.1){RRC1R = 5;}
else if(RRR1 >= 0.1 && RRR1 < 1){RRC1R = 0;}
else if(RRR1 >= 1 && RRR1 < 3){RRC1R = 1;}
else if(RRR1 >= 3){RRC1R = 2;}
else {RRC1R = 'Boo';}
if (!isNaN(RRC1R))
{
document.getElementById('RogerRatio').value = RRC1R;
}
}
</script>
I am trying to create a bmi calculator and tried the following .
$(document).ready(function() {
// Convert ft to inches
function convertHeight(ft) {
return ft * 12;
}
// calculate total height
function showbmi() {
var weight = document.getElementById('weight').value * 1;
var height_ft = document.getElementById('height_ft').value * 1;
var height_in = document.getElementById('height_in').value * 1;
var height = convertHeight(height_ft) + height_in;
var female_h = (height * height) / 30;
var male_h = (height * height) / 28;
var gender;
var x = document.getElementsByName("gender");
for (var i = 0; i < x.length; i++) {
if (x[i].checked == true) {
gender = x[i].value;
break;
}
}
var bmi = "?";
if (gender == "female") {
bmi = (Math.round((weight * 703) / (height * height)));
if (isNaN(bmi)) bmi = "?";
} else {
bmi = (Math.round((weight * 703) / (height * height)));
if (isNaN(bmi)) bmi = "?";
}
var bmi_msg = "?";
if (bmi < 15) {
bmi_msg = "Very severely underweight";
} else if (bmi <= 16) {
bmi_msg = "Severely underweight";
} else if (bmi <= 18.4) {
bmi_msg = "Underweight";
} else if (bmi <= 24.9) {
bmi_msg = "Normal";
} else if (bmi <= 29.9) {
bmi_msg = "Overweight";
} else if (bmi <= 34.9) {
bmi_msg = "Obese Class I (Moderately obese)";
} else if (bmi <= 39.9) {
bmi_msg = "Obese Class II (Severely obese)";
} else {
bmi_msg = "Obese Class III (Very severely obese)";
}
$("#result").text(bmi);
return bmi;
$("#comment").text(bmi_msg);
return bmi_msg;
}
//bmi infos
//finish
$("form#calc input").bind("keydown", function() {
setTimeout(function() {
showbmi();
}, 100);
return true;
});
$("form#calc input").bind("change", function() {
showbmi();
});
$("form#calc radio").bind("change", function() {
showbmi();
});
$("form#calc").bind("submit", function() {
showbmi();
return false;
});
});
The bmi displays correctly, but the messages forbmi values are not displaying. i checked the console and it says code unreachable.
Can somebody throw some light where I am goin wrong, and How I can improve this code.
You have a return statement after setting the result content before setting the comment, so $("#comment").text(bmi_msg); is never executed.
A function will return control to the caller method as soon as a return statement is executed, so there should be only one return statement in an execution flow.
So remove return bmi; from the code for the comment setting to work
$(document).ready(function() {
// Convert ft to inches
function convertHeight(ft) {
return ft * 12;
}
// calculate total height
function showbmi() {
var weight = $('#weight').val() * 1;
var height_ft = $('#height_ft').val() * 1;
var height_in = $('#height_in').val() * 1;
var height = convertHeight(height_ft) + height_in;
var female_h = (height * height) / 30;
var male_h = (height * height) / 28;
var gender = $('input[name="gender"]:checked').val();
var bmi = "?";
if (gender == "female") {
bmi = (Math.round((weight * 703) / (height * height)));
if (isNaN(bmi)) bmi = "?";
} else {
bmi = (Math.round((weight * 703) / (height * height)));
if (isNaN(bmi)) bmi = "?";
}
var bmi_msg = "?";
if (bmi < 15) {
bmi_msg = "Very severely underweight";
} else if (bmi <= 16) {
bmi_msg = "Severely underweight";
} else if (bmi <= 18.4) {
bmi_msg = "Underweight";
} else if (bmi <= 24.9) {
bmi_msg = "Normal";
} else if (bmi <= 29.9) {
bmi_msg = "Overweight";
} else if (bmi <= 34.9) {
bmi_msg = "Obese Class I (Moderately obese)";
} else if (bmi <= 39.9) {
bmi_msg = "Obese Class II (Severely obese)";
} else {
bmi_msg = "Obese Class III (Very severely obese)";
}
$("#result").text(bmi);
$("#comment").text(bmi_msg);
return bmi;
}
//bmi infos
var timer;
$("#calc input").bind("keydown change", function() {
clearTimeout(timer);
timer = setTimeout(function() {
showbmi();
}, 100);
return true;
});
$("#calc").bind("submit", function() {
clearTimeout(timer);
showbmi();
return false;
});
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<form id="calc">
<input id="weight" />
<input id="height_ft" />
<input id="height_in" />
<br />
<input name="gender" type="radio" value="male" />
<input name="gender" type="radio" value="female" />
<br />
<input type="submit" value="Check" />
<div id="result"></div>
<div id="comment"></div>
</form>
Note: Since you are using jQuery, why not use its utility methods to get/set the values/content
I'm wanting to use jQuery to dynamically modify the background color of a <div>, based on its CSS property, width value.
The usage is for some form of color-coded meter, which indicates how well (or poorly) a device performs in a specific category (there are 5), and then there is one 'overall' category, which produces the overall score, based on a little math (add all 5 together and divide the answer by 5).
I have tried two methods, one based on the little jQuery knowledge I have, and the other adapted from an answer on SO. Both are included in the JSFiddle I have provided, with the latter commented out.
Here are the colors and the ranges of the widths for each:
0-24% = red - #a41818
25-49% = orange - #87581c
50-74% = yellow - #997815
75-90% = yellowgreen - #7ba01c
91-100% = green - #3a8d24
Thanks!
I'd suggest:
$('.rating-bar').css('background-color', function(){
var percentage = parseInt($(this).data('size'), 10);
if (percentage > 0 && percentage < 25){
return '#a41818'
}
else if (percentage > 24 && percentage < 50) {
return '#87581c';
}
else if (percentage > 49 && percentage < 75) {
return '#997815';
}
else if (percentage > 74 && percentage < 90) {
return '#7ba01c';
}
else if (percentage > 89 && percentage <= 100) {
return '#3a8d24';
}
});
JS Fiddle demo.
There were a few semantic problems ($(e) used instead of $(this), ($(document).ready nested strangely), and the logic you've used requires the ratio of each bar's width to the parent bar, not the width itself.
Try this: http://jsfiddle.net/VFSUN/1/
$(document).ready(function () {
var bar = $('.rating-bar');
bar.css("background-color", "#7ba01c");
var parentWidth = parseInt($('.rating-bar-bg').css("width"));
$('.rating-bar').each(function () {
var e = $(this).css("width");
e = parseInt(e);
ratio = e / parentWidth * 100;
if (ratio <= 24) {
$(this).css("background-color", "#a41818");
} else if (ratio >= 25 && e < 50) {
$(this).css("background-color", "#87581c");
} else if (ratio >= 50 && e < 75) {
$(this).css("background-color", "#997815");
} else if (e >= 75 && e < 91) {
$(this).css("background-color", "#7ba01c");
} else if (ratio >= 91) {
$(this).css("background-color", "#3a8d24");
}
});
});
I suggest you are starting at wrong point by checking width, when in fact you need to be setting width based on the data-size attribute. This size can then be used to set bckground color
$(document).ready(function() {
$('.rating-bar').each(function(){
var $bar=$(this), size=$bar.data('size');
$bar.width(size+'%').css("background-color", getBackground( size))
});
});
function getBackground( e){
var color= "#a41818";/* < 24*/
if (e >= 25 && e < 50) {
color= "#87581c";
} else if (e >= 50 && e < 75) {
color= "#997815";
} else if (e >= 75 && e < 91) {
color= "#7ba01c";
} else if (e >= 91) {
color= "#3a8d24";
}
return color
}
DEMO