Description of the situation:
I have time inputs described in class = "start" and time inputs described in class = "end". The difference is calculated based on the equation 'end-start = actual' 'Actual' are time inputs. Actual inputs should add up and write to the input with id = "sum_actual" (type = text)
Problem:
I have two problems in this question:
a) I don't know how to loop the code that sums up individual inputs class = actual, ultimately there are over 30
b) the code is written in such a way that I have to enter the time manually in the input class = actual so that it updates to the whole sum, when the result comes automatically calculated, it does not add up
Notes:
ultimately, class = actual inputs will be readonly
ultimately there are several other columns with inputs (unimportant
) between the start and end and actual inputs (cosmetics, I pay attention to the way of writing the code)
My code/I tried this code above in javascript is to be improved
//code that sums values from the actual class / should loop it
function msToTime(duration) {
var minutes = Math.floor((duration / (1000 * 60)) % 60),
hours = Math.floor(duration / (1000 * 60 * 60));
hours = hours < 10 ? "0" + hours : hours;
minutes = minutes < 10 ? "0" + minutes : minutes;
return hours + ":" + minutes;
}
console.log(msToTime(300000));
function sum_diff() {
zxc = document.getElementById("actual_1").value;
xcz = document.getElementById("actual_2").value;
czx = document.getElementById("actual_3").value;
zxc = zxc.split(":");
xcz = xcz.split(":");
czx = czx.split(":");
var zxcDate = new Date(0, 0, 0, zxc[0], zxc[1], 0);
var xczDate = new Date(0, 0, 0, xcz[0], xcz[1], 0);
var czxDate = new Date(0, 0, 0, czx[0], czx[1], 0);
var zxcMs =
zxcDate.getHours() * 60 * 60 * 1000 +
zxcDate.getMinutes() * 60 * 1000;
var xczMs =
xczDate.getHours() * 60 * 60 * 1000 +
xczDate.getMinutes() * 60 * 1000;
var czxMs =
czxDate.getHours() * 60 * 60 * 1000 +
czxDate.getMinutes() * 60 * 1000;
var ms = zxcMs + xczMs + czxMs;
return msToTime(ms);
}
var elements = document.getElementsByClassName("actual");
for (var i = 0; i < elements.length; i++) {
elements[i].addEventListener("change", function(e) {
document.getElementById("sum_actual").value = sum_diff();
});
}
// code calculating differences start from end and writing to actual / do not touch it
function diff(start, end) {
start = start.split(":");
end = end.split(":");
const startDate = new Date(0, 0, 0, start[0], start[1], 0);
const endDate = new Date(0, 0, 0, end[0], end[1], 0);
let diff = endDate.getTime() - startDate.getTime();
const hours = Math.floor(diff / 1000 / 60 / 60);
diff -= hours * 1000 * 60 * 60;
const minutes = Math.floor(diff / 1000 / 60);
return (hours < 9 ? "0" : "") + hours + ":" + (minutes < 9 ? "0" : "") + minutes;
}
document.querySelector('table').addEventListener('change', function (e) {
const classList = e.target.classList
if (classList.contains('start') || classList.contains('end')) {
//retrieve the associated inputs
const tr = e.target.parentNode.parentNode
const [start, end, actual] = [...tr.querySelectorAll('.start,.end,.actual')]
const value = diff(start.value, end.value)
actual.value = value
}
})
<table>
<tbody>
<tr>
<td><input class="start" type="time"/></td>
<td><input class="end" type="time"/></td>
<td><input class="actual" type="time" id="actual_1" value="00:00" /></td>
</tr>
<tr><td><input class="actual" type="time" id="actual_2" value="00:00" /></td></tr>
<tr><td><input class="actual" type="time" id="actual_3" value="00:00" /></td></tr>
</tbody>
<tfoot>
<tr><th><input type="text" id="sum_actual" readonly /></th></tr>
</tfoot>
</table>
Note:
thanks, it turned out that I have a problem with the counting script and it counts only to 99:59, can you change this limit? to show hours until 300:00?
When confronted with this type of situations, I like to break my big problem into really small problems, and make really small functions which do one thing, but do it well:
const actuals = [...document.getElementsByClassName("actual")];
document.querySelector('table').addEventListener('change', function(e) {
const classList = e.target.classList;
if (classList.contains('start') || classList.contains('end')) {
//retrieve the associated inputs
const tr = e.target.parentNode.parentNode;
const [start, end, actual] = [...tr.querySelectorAll('.start,.end,.actual')];
const value = diff(start.value, end.value);
actual.value = value;
updateActualSum();
}
});
// Update total duration once on load
updateActualSum();
function msToTime(duration) {
const minutes = Math.floor((duration / (1000 * 60)) % 60),
hours = Math.floor(duration / (1000 * 60 * 60));
return twoOrMoreDigits(hours) + ":" + twoOrMoreDigits(minutes);
}
function twoOrMoreDigits(n) {
return n < 10 ? '0' + n : n;
}
function timeToMs(time) {
if (time) { // may be "" if the value is not set
const [hours, minutes] = time.split(":").map(str => parseInt(str, 10));
return (hours * 60 + minutes) * 60 * 1000;
}
return 0;
}
function sum_diff() {
const sum = actuals.reduce((acc, el) => acc + timeToMs(el.value), 0);
return msToTime(sum);
}
function diff(start, end) {
return msToTime(timeToMs(end) - timeToMs(start));
}
function updateActualSum() {
document.getElementById('sum_actual').value = sum_diff();
}
body {font-family: Arial, Helvetica, sans-serif; } table { border-collapse: collapse; } td, th { border: 1px solid #ddd; padding: .2rem; } input[readonly] { background: #f5f5f5; border: none; font-family: inherit; text-align: center; padding: .2rem; }
<table>
<tbody>
<thead>
<tr>
<th>Start</th>
<th>End</th>
<th>Duration</th>
</tr>
</thead>
<tr>
<td><input class="start" type="time" /></td>
<td><input class="end" type="time" /></td>
<td><input class="actual" type="time" id="actual_1" value="00:00" readonly /></td>
</tr>
<tr>
<td><input class="start" type="time" /></td>
<td><input class="end" type="time" /></td>
<td><input class="actual" type="time" id="actual_2" value="00:00" readonly /></td>
</tr>
<tr>
<td><input class="start" type="time" /></td>
<td><input class="end" type="time" /></td>
<td><input class="actual" type="time" id="actual_2" value="00:00" readonly /></td>
</tr>
<tr>
<td><input class="start" type="time" /></td>
<td><input class="end" type="time" /></td>
<td><input class="actual" type="time" id="actual_2" value="00:00" readonly /></td>
</tr>
<tr>
<td><input class="start" type="time" /></td>
<td><input class="end" type="time" /></td>
<td><input class="actual" type="time" id="actual_3" value="00:00" readonly /></td>
</tr>
</tbody>
<tfoot>
<tr>
<th colspan="3"><input type="text" id="sum_actual" readonly /></th>
</tr>
</tfoot>
</table>
Get all actual values in an array and then using moment, calculate the sum duration
//const durations = document.getElementsByClassName('actual').map(input => input.value);
// And let say, we got following durations
const durations = ['01:30', '05:00', '10:20'];
const totalDuration = durations.slice(1)
.reduce((prev, cur) => moment.duration(cur).add(prev),
moment.duration(durations[0]));
const requiredOutput = `${totalDuration.asHours().toFixed(0)}:${totalDuration.minutes().toString().padStart(2, '0')}`;
console.log(requiredOutput)
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.24.0/moment.min.js"></script>
Related
I have an HTML input range that uses JS to change the text content of a table below it based on the value of the input range. It is working on Desktop but on Mobile the content will be stuck at the specified content for value = 1 and won't change to 2, 3, and 4. Any ideas? Thank you!
<div class="range-wrap">
<input type="range" class="range" id="slide" value="1" min="1" max="4" />
<output class="bubble"></output>
</div>
<h3 class="you-will">Every week you will save...</h3>
<table class="save">
<tr>
<td><img class="eco-icon" src="https://www.happynest.com/hubfs/14500231/eco-icons-01.png" alt="time" / ></td>
<td class="eco-text"><strong><span id="time"></span></strong> <span>Spend time outdoors!</span></td>
</tr>
<tr>
<td><img class="eco-icon" src="https://www.happynest.com/hubfs/14500231/eco-icons-02.png" alt="water" / ></td>
<td class="eco-text"><strong><span id="water"></span></strong> <span>Save the environment.</span></td>
</tr>
<tr>
<td><img class="eco-icon" src="https://www.happynest.com/hubfs/14500231/eco-icons-03.png" alt="electricity" / ></td>
<td class="eco-text"><strong><span id="electric"></span></strong> <span>Save some money.</span></td>
</tr>
</table>
JS
function updateSlider() {
var value = $("#slide").val();
if (value == 1) {
$("#time").text("4 hours of time.");
$("#water").text("60 gallons of water.");
$("#electric").text("$5 in electric bills.");
} else if (value == 2) {
$("#time").text("8 hours of time.");
$("#water").text("120 gallons of water.");
$("#electric").text("$10 in electric bills.");
} else if (value == 3) {
$("#time").text("12 hours of time.");
$("#water").text("180 gallons of water.");
$("#electric").text("$15 in electric bills.");
} else if (value == 4) {
$("#time").text("16 hours of time.");
$("#water").text("240 gallons of water.");
$("#electric").text("$20 in electric bills.");
}
}
updateSlider();
$('#slide').mousemove(updateSlider);
const allRanges = document.querySelectorAll(".range-wrap");
allRanges.forEach(wrap => {
const range = wrap.querySelector(".range");
const bubble = wrap.querySelector(".bubble");
range.addEventListener("input", () => {
setBubble(range, bubble);
});
setBubble(range, bubble);
});
function setBubble(range, bubble) {
const val = range.value;
const min = range.min ? range.min : 0;
const max = range.max ? range.max : 100;
const newVal = Number(((val - min) * 100) / (max - min));
bubble.innerHTML = val;
// Sorta magic numbers based on size of the native UI thumb
bubble.style.left = `calc(${newVal}% + (${8 - newVal * 1.5}px))`;
}
The input range is changing the table's content on Desktop but not mobile
jQuery(document).ready(function() {
jQuery().invoice({
addRow: "#addRow",
delete: ".delete",
parentClass: ".item-row",
no: ".no",
price: ".price",
qty: ".qty",
total: ".total",
totalQty: "#totalQty",
subtotal: "#subtotal",
discount: "#discount",
shipping: "#shipping",
grandTotal: "#grandTotal"
});
});
(function(jQuery) {
$.opt = {}; // jQuery Object
jQuery.fn.invoice = function(options) {
var ops = jQuery.extend({}, jQuery.fn.invoice.defaults, options);
$.opt = ops;
var inv = new Invoice();
inv.init();
jQuery('body').on('click', function(e) {
var cur = e.target.id || e.target.className;
if (cur == $.opt.addRow.substring(1))
inv.newRow();
if (cur == $.opt.delete.substring(1))
inv.deleteRow(e.target);
inv.init();
});
jQuery('body').on('keyup', function(e) {
inv.init();
});
return this;
};
}(jQuery));
function Invoice() {
self = this;
}
Invoice.prototype = {
constructor: Invoice,
init: function() {
this.calcTotal();
this.calcTotalQty();
this.calcSubtotal();
this.calcGrandTotal();
},
/**
* Calculate total price of an item.
*
* #returns {number}
*/
calcTotal: function() {
jQuery($.opt.parentClass).each(function(i) {
var row = jQuery(this);
var total = row.find($.opt.price).val() * row.find($.opt.qty).val();
total = self.roundNumber(total, 2);
row.find($.opt.total).val(total);
});
return 1;
},
/***
* Calculate total quantity of an order.
*
* #returns {number}
*/
calcTotalQty: function() {
var totalQty = 0;
jQuery($.opt.qty).each(function(i) {
var qty = jQuery(this).val();
if (!isNaN(qty)) totalQty += Number(qty);
});
totalQty = self.roundNumber(totalQty, 2);
jQuery($.opt.totalQty).val(totalQty);
return 1;
},
/***
* Calculate subtotal of an order.
*
* #returns {number}
*/
calcSubtotal: function() {
var subtotal = 0;
jQuery($.opt.total).each(function(i) {
var total = jQuery(this).val();
if (!isNaN(total)) subtotal += Number(total);
});
subtotal = self.roundNumber(subtotal, 2);
jQuery($.opt.subtotal).val(subtotal);
return 1;
},
/**
* Calculate grand total of an order.
*
* #returns {number}
*/
calcGrandTotal: function() {
var grandTotal = Number(jQuery($.opt.subtotal).val()) +
Number(jQuery($.opt.shipping).val()) -
Number(jQuery($.opt.discount).val());
grandTotal = self.roundNumber(grandTotal, 2);
jQuery($.opt.grandTotal).val(grandTotal);
return 1;
},
/**
* Add a row.
*
* #returns {number}
*/
newRow: function() {
jQuery(".item-row:last").after('<tr class="item-row"><td class="item-name"><div class="delete-btn"><a class=' + $.opt.delete.substring(1) + ' href="javascript:;" title="Remove row">X</a><input type="text" class="form-control no p-0 text-center" placeholder="No" type="text"></div></td><td class="item-name"><input type="text" class="form-control item p-0" placeholder="Item" type="text"></td><td><input class="form-control qty p-0 text-center" placeholder="Qty" type="text"></td><td><input class="form-control price p-0 text-center" placeholder="Price" type="text"></td><td><input class="form-control total" type="text" readonly></td></tr>');
if (jQuery($.opt.delete).length > 0) {
jQuery($.opt.delete).show();
}
return 1;
},
/**
* Delete a row.
*
* #param elem current element
* #returns {number}
*/
deleteRow: function(elem) {
jQuery(elem).parents($.opt.parentClass).remove();
if (jQuery($.opt.delete).length < 2) {
jQuery($.opt.delete).hide();
}
return 1;
},
/**
* Round a number.
* Using: http://www.mediacollege.com/internet/javascript/number/round.html
*
* #param number
* #param decimals
* #returns {*}
*/
roundNumber: function(number, decimals) {
var newString; // The new rounded number
decimals = Number(decimals);
if (decimals < 1) {
newString = (Math.round(number)).toString();
} else {
var numString = number.toString();
if (numString.lastIndexOf(".") == -1) { // If there is no decimal point
numString += "."; // give it one at the end
}
var cutoff = numString.lastIndexOf(".") + decimals; // The point at which to truncate the number
var d1 = Number(numString.substring(cutoff, cutoff + 1)); // The value of the last decimal place that we'll end up with
var d2 = Number(numString.substring(cutoff + 1, cutoff + 2)); // The next decimal, after the last one we want
if (d2 >= 5) { // Do we need to round up at all? If not, the string will just be truncated
if (d1 == 9 && cutoff > 0) { // If the last digit is 9, find a new cutoff point
while (cutoff > 0 && (d1 == 9 || isNaN(d1))) {
if (d1 != ".") {
cutoff -= 1;
d1 = Number(numString.substring(cutoff, cutoff + 1));
} else {
cutoff -= 1;
}
}
}
d1 += 1;
}
if (d1 == 10) {
numString = numString.substring(0, numString.lastIndexOf("."));
var roundedNum = Number(numString) + 1;
newString = roundedNum.toString() + '.';
} else {
newString = numString.substring(0, cutoff) + d1.toString();
}
}
if (newString.lastIndexOf(".") == -1) { // Do this again, to the new string
newString += ".";
}
var decs = (newString.substring(newString.lastIndexOf(".") + 1)).length;
for (var i = 0; i < decimals - decs; i++)
newString += "0";
//var newNumber = Number(newString);// make it a number if you like
return newString; // Output the result to the form field (change for your purposes)
}
};
.delete-btn {
position: relative;
}
.delete {
display: block;
color: #000;
text-decoration: none;
position: absolute;
background: #EEEEEE;
font-weight: bold;
padding: 2px 8px;
border: none;
border-radius: 5px;
top: -1px;
left: -16px;
font-family: Verdana;
font-size: 15px;
}
<script src="https://code.jquery.com/jquery-3.6.0.slim.js"></script>
<script src="https://cdn.metroui.org.ua/v4/js/metro.min.js"></script>
<link href="https://cdn.metroui.org.ua/v4/css/metro-all.min.css" rel="stylesheet" />
<table class="table compact table-border cell-border">
<tr class="item-row">
<th class="headNo">No</th>
<th class="headItem">Item</th>
<th class="headQty">Qty</th>
<th class="headPrice">Price</th>
<th class="headTotal">Amount</th>
</tr>
<tbody id="row">
<tr id="hiderow">
<td colspan="5">
<a id="addRow" href="javascript:;" title="Add a row" class="button">+</a>
</td>
</tr>
<tr>
<td></td>
<td></td>
<td></td>
<td class="text-right"><strong>Sub Total</strong></td>
<td><input id="subtotal"></td>
</tr>
<tr>
<td><strong>Total Quantity: </strong><span id="totalQty">0</span> Units</td>
<td></td>
<td></td>
<td class="text-right"><strong>Discount</strong></td>
<td><input class="form-control" id="discount" value="0" type="text"></td>
</tr>
<tr>
<td></td>
<td></td>
<td></td>
<td class="text-right"><strong>Shipping</strong></td>
<td><input class="form-control" id="shipping" value="0" type="text"></td>
</tr>
<tr>
<td colspan="3"></td>
<td class="text-center"><strong>Total</strong></td>
<td><input id="grandTotal" class="form-control text-right" type="text" readonly></td>
</tr>
</tbody>
</table>
<button id="btn">Submin</button>
code of invoice in snippet
"No,Item,qty,Price,Amount" works prefect in client side and value of "No,Item,qty,Price,Amount" shown in console but i want to transfer "No,Item,qty,Price,Amount" values in google sheet. I tried using jquery but it only transfer one row data and repeat first row data in second row.How can i do that?
I want to write code for a counter which countdown from 4 hours in hour, minute and second components (3:59:59 ... 3:59:58 ..... 0:0:1 ... 0:0:0) in which the user can increment or decrement any of those components by using +/-icons. I wrote a code but I cannot make it? How can I complete it? In my code just increase/decrease icon works.
function increment() {
hour = parseInt(document.getElementsByName("hour")[0].value);
minute = parseInt(document.getElementsByName("minute")[0].value);
second = parseInt(document.getElementsByName("second")[0].value);
if (second + 1 == 61) {
minute = minute + 1;
if (minute == 61) {
hour = hour + 1;
if (hour == 3) {
hour = 0;
}
minute = 0;
}
second = 0;
} else {
second += 1;
}
document.getElementsByName("hour")[0].value = hour.toString();
document.getElementsByName("minute")[0].value = minute.toString();
document.getElementsByName("second")[0].value = second.toString();
}
function decrement() {
hour = parseInt(document.getElementsByName("hour")[0].value);
minute = parseInt(document.getElementsByName("minute")[0].value);
second = parseInt(document.getElementsByName("second")[0].value);
if (second - 1 <= 0) {
minute -= 1;
if (minute <= 0) {
hour -= 1;
if (hour <= 0) {
hour = 2;
minute = 60;
} else {
minute = 60;
}
}
second = 60;
} else {
second -= 1;
}
document.getElementsByName("hour")[0].value = hour.toString();
document.getElementsByName("minute")[0].value = minute.toString();
document.getElementsByName("second")[0].value = second.toString();
}
<html>
<body>
<table>
<tr>
<td>Hour</td>
<td>
<input type="text" name = "hour" placeholder = "HOUR" value="0"/>
</td>
<td>Minute</td>
<td>
<input type="text" name="minute" placeholder="MINUTE" value="0"/>
</td>
<td>Second</td>
<td>
<input type="text" name="second" placeholder="SECOND" value="0"/>
</td>
</tr>
<tr>
<td><br>
<input type="button" name="+" value="+" onclick= "return increment()"/>
</td>
<td><br>
<input type="button" name="-" value="-" onclick="return decrement()"/>
</td>
</tr>
</table>
</body>
</html>
Here is how i would go about it, first of all, you had the script execute before the body which caused problems since the elements you were trying to select didn't exist yet,
also, you're trying to do everything everywhere, but if you keep state, it is much more manageable
<script>
const maxTime = 4 * 60 * 60; // 4 hours in seconds
const hours = document.querySelector("[name='hour']");
const minutes = document.querySelector("[name='minute']");
const seconds = document.querySelector("[name='second']");
let remaining = maxTime;
let counter = setInterval(decrement, 1000);
function increment() {
remaining = Math.max(maxTime, remaining + 1);
display();
}
function decrement() {
remaining = Math.max(0, remaining - 1);
display();
}
function display() {
const time = new Date(remaining * 1000).toISOString();
hours.value = time.slice(11, 13);
minutes.value = time.slice(14, 16);
seconds.value = time.slice(17, 19);
}
</script>
You can use the setInterval function
This will call your decrement function every second
setInterval(decrement, 1000)
The first parameter is the function to be executed.
The second parameter indicates the number of milliseconds before execution.
I have a countdown timer for clicking a button at 0 second and it works but I want the time itself to reset after the click. Using this code I want Time 1 to be reset when work2 is clicked
function toTimeString(seconds) {
return (new Date(seconds * 1000)).toUTCString().match(/(:\d\d:\d\d)/)[0];
}
function startTimer() {
var nextElem = $(this).parents('td').next();
var duration = nextElem.text();
var a = duration.split(':');
var seconds = (+a[0]) * 60 * 60 + (+a[1]) * 60 + (+a[2]);
setInterval(function() {
seconds--;
if (seconds >= 0) {
nextElem.html(toTimeString(seconds));
}
if (seconds === 0) {
document.getElementById('work2').click();
clearInterval(seconds);
}
}, 1000);
}
$('.lazy').on('click', startTimer);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<table>
<tr>
<td>
<input id="work1" class="lazy" type="button" value="Time 1"/>
</td>
<td>:00:05</td>
</tr>
<tr>
<td>
<input id="work2" class="lazy" type="button" value="Time 2" type="button" />
</td>
<td>:10:00</td>
</tr>
</table>
Timers are used like this: You start the timer and store the timer handle in a variable. When stopping the timer, you hand over this variable.
var timer1 = setInterval(function(){}, 1000);
..
clearInterval(timer1);
I'm sorry to post this question but I'm kinda newbie when it comes to js. I have created a simple page that will compute charging transactions, so what it will do is to simply multiply the Quantity and Price to .25%. But here is the trick, if the total product is less than 50 the Charge field should default to 50 and that's where I'm kinda lost,
here is my code:
<tr>
<td width="144">Quantity:</td>
<td width="63"><input type="text" name="quantity" id="quantity" size="8"/></td>
</tr>
<tr>
<td>Price:</td>
<td><input type="text" name="price" id="price" size="8"/></td>
</tr>
<tr>
<td colspan="4"><strong>Charges:</strong></td>
</tr>
<tr>
<td>Charge:</td>
<td><input style="color:#F00" type="text" name="charge" id="charge" size="8" readonly="readonly" /></td>
<td colspan="2">Quantity x Price x .25% OR 20 whichever is higher</td>
</tr>
here is the js that i managed to have,
$(function () {
$("#quantity, #price").keyup(function () {
var q = parseFloat($("#quantity").val()); // Quantity
var p = parseFloat($("#price").val()); // Price
if (isNaN(q) || isNaN(p) || q<=0 || p <= 0) {
$("#charge").val('');
return false;
}
$("#charge").val((q * p * 0.0025).toFixed(3)); // Charge
});
});
Put the total in a variable and test it before putting it into the DOM:
$(function () {
$("#quantity, #price").keyup(function () {
var q = parseFloat($("#quantity").val()); // Quantity
var p = parseFloat($("#price").val()); // Price
if (isNaN(q) || isNaN(p) || q<=0 || p <= 0) {
$("#charge").val('');
return false;
}
var total = q * p * 0.0025;
if (total < 50) {
total = 50;
}
$("#charge").val(total.toFixed(3)); // Charge
});
});
Another way is to use Math.max():
$("#charge").val(Math.max(50, q * p * 0.0025).toFixed(3)); // Charge