I'm making a calculator and I'm stumbling at the first JS hurdle. I'm trying to set up event listeners on the number buttons so that a functions runs on click, but I get the error that I can't set up an event handler on null. But I can't see where I am going wrong!?
I am trying to organise my code as I go along similar to an online course that I did - separating out all the functions and trying to keep it easy to read.
See code and thanks in advance:
//TOTAL CONTROLLER
var totalController = (function () {
})();
//UI CONTROLLER
var UIController = (function () {
var DOMstrings = {
calcbuttons: '.calcButtons',
number: '.number',
operator: '.operator',
equals: '.equals',
total: '#total'
};
return {
getDOMstrings: function () {
return DOMstrings;
}
};
})();
//GLOBAL CONTROLLER
var controller = (function (totalCTRL, UICtrl) {
var setupEventListeners = function () {
var DOM = UICtrl.getDOMstrings();
var el = document.querySelector(".calcButtons");
el.addEventListener('click', function (e) {
//var table = e.target;
//if (table.classList)
console.log(e);
//ctrlAddNumber();
});
// click operator
//click C
//click AC
//click equals
};
var ctrlAddNumber = function () {
var selectedNumber;
//1. get number from html from click
selectedNumber = document.querySelector(DOM.number).innerHTML;
console.log(number);
//2. add to display
};
return {
init: function () {
console.log('Application has started.');
setupEventListeners();
}
};
})(totalController, UIController);
controller.init();
<!DOCTYPE html>
<html>
<head>
<script src="https://code.jquery.com/jquery-3.2.1.min.js" integrity="sha256-hwg4gsxgFZhOsEEamdOYGBf13FyQuiTwlAQgxVSNgt4=" crossorigin="anonymous"></script>
<link type="text/css" rel="stylesheet" href="calc_style.css" />
<style>
#import url('https://fonts.googleapis.com/css?family=Lato');
</style>
<script type='text/javascript' src='calcjs.js'></script>
<title>JavaScript Calculator</title>
</head>
<body>
<div id="container">
<div id="calculator">
<div id=t otal></div>
<table class="calcButtons" cellspacing="0" cellpadding="0">
<tr>
<td class="wide">AC</td>
<td class="wide">←</td>
<td class="wide"></td>
<td class="operator"></td>
</tr>
<tr>
<td class="number">7</td>
<td class="number">8</td>
<td class="number">9</td>
<td class="operator">÷</td>
</tr>
<tr>
<td class="number">4</td>
<td class="number">5</td>
<td class="number">6</td>
<td class="operator">×</td>
</tr>
<tr>
<td class="number">1</td>
<td class="number">2</td>
<td class="number">3</td>
<td class="operator">-</td>
</tr>
<tr>
<td class="number">0</td>
<td>.</td>
<td id="equals" class="equals">=</td>
<td class="operator">+</td>
</tr>
</table>
</div>
</div>
</body>
</html>
A couple of things:
Move your JS out of the head and just below where you close the body. The script tag is blocking and the browser won't continue with rendering your site till it has processed the script element.
If you want an element to be interactive, try to use an element that was meant for it. Use an anchor (a) element to navigate betweens documents and buttons (button) for most other interactions.
An HTML table needs a tbody.
Your JS code is quite a lot and I didn't have time to go through all of it so I made this small example on how it could work. I hope you can translate it to your situation.
const
calcButtons = document.querySelector('.calcButtons');
function onCalcButtonClicked(event) {
debugger;
const
clickedButton = event.target;
if (clickedButton.hasAttribute('data-number')) {
console.log('The user inputted a number: ', clickedButton.getAttribute('data-number'));
} else if (clickedButton.hasAttribute('data-action')) {
console.log('The user selected an action: ', clickedButton.getAttribute('data-action'));
}
}
calcButtons.addEventListener('click', onCalcButtonClicked);
<div id="container">
<div id="calculator">
<div id="total"></div>
<table class="calcButtons" cellspacing="0" cellpadding="0">
<tbody>
<tr>
<td><button type="button" data-action="" class="wide">AC</button></td>
<td><button type="button" data-action="" class="wide">←</button></td>
<td><button type="button" data-action="" class="wide"></button></td>
<td><button type="button" data-action="" class="operator"></button></td>
</tr>
<tr>
<td><button type="button" data-number="7" class="number">7</button></td>
<td><button type="button" data-number="8" class="number">8</button></td>
<td><button type="button" data-number="9" class="number">9</button></td>
<td><button type="button" data-action="plus" class="operator">÷</button></td>
</tr>
<tr>
<td><button type="button" data-number="4" class="number">4</button></td>
<td><button type="button" data-number="5" class="number">5</button></td>
<td><button type="button" data-number="6" class="number">6</button></td>
<td><button type="button" data-action="times" class="operator">×</button></td>
</tr>
<tr>
<td><button type="button" data-number="1" class="number">1</button></td>
<td><button type="button" data-number="2" class="number">2</button></td>
<td><button type="button" data-number="3" class="number">3</button></td>
<td><button type="button" data-action="minus" class="operator">-</button></td>
</tr>
<tr>
<td><button type="button" data-number="0" class="number">0</button></td>
<td><button type="button" data-number=".">.</button></td>
<td><button type="button" data-action="equals" id="equals" class="equals">=</button></td>
<td><button type="button" data-action="divide" class="operator">+</button></td>
</tr>
</tbody>
</table>
</div>
</div>
Related
When I click on my button "Select" it should show me the HTML popup, and for some reason is not happening.
Could it be some id problem or hard code?
The main idea is to click and bring some kind of list reading from a random array list.
Below: my .js with the call back id and display.
Any ideas?
<!-- This hosts all HTML templates that will be used inside the JavaScript code -->
<table class ="cls-{id} active-{active}" style="display: none;" width="100%" id="rowTemplate">
<tr class ="bb cls-{id} active-{active}">
<td class="active-{active}" id="{id}-question" width="70%">{question}</td>
<td class="cls-{id} active-{active}" width="30%">
<button class="buttons" step="0.01" data-clear-btn="false" style="background: #006b54; color:white !important ;" id="{id}-inspectionResult"></button>
</td>
</tr>
</table>
<div id="projectPopUp" class="popup-window" style="display:none">
<div class="popuptitle" id="details-name"></div>
<table width="100%" id="detailsgrid">
<tr>
<td style="text-align:left">Start Time</td>
<td> <select id="details-startTime" data-role="none"></select></td>
</tr>
<tr>
<td style="text-align:left">End Time</td>
<td> <select id="details-endTime" data-role="none"></select></td>
</tr>
</table>
<div>
<button class="smallButton" onClick="closeProjectPopup()">Cancel</button>
<button class="smallButton" onClick="submitProjectPopup()">Submit</button>
</div>
</div>
<table style="display: none;" id="sectionRowTemplate">
<tr width="100%" class="bb cls-{id}-row2 sectionheader">
<td class="cls-{id}" colspan="3">{question}</td>
</tr>
</table>
Javascript code:
var buildQuestionnaire = function(){
parseInitialDataHolder();
for (var i = 0; i < ARRAY_OF_QUESTIONS.length; i++){
var id = i;
var data = {
id: id,
question: ARRAY_OF_QUESTIONS[i].question,
inspectionResult: '',
active: true
};
var initialdata = initialdataholder[id];
if(initialdata) {
data = initialdata;
}
dataholder.push(data);
if (typeof ARRAY_OF_QUESTIONS[i].header == 'undefined') {
$('#questionsTable tbody').append(Utils.processTemplate("#rowTemplate tbody", data));
$("#" + id + "-inspectionResult").text(data.inspectionResult || 'Select');
$("#" + id + "-inspectionResult").click(resultHandler.bind(data));
updateActiveStatus(data);
commentvisibilitymanager(data);
}
else {
$('#questionsTable tbody').append(Utils.processTemplate("#sectionRowTemplate tbody", data));
}
}
}
//to show the popup
$('#projectPopUp').show();
//to close the popup
$('#projectPopUp').hide();
$(document).ready(function() {
buildQuestionnaire();
});
I have got a problem. I need to add lines in the table and give the numbered classes, like name_4, name_5 and so on using javascript or jquery.
<body>
<form action="" method="post">
<button>Добавить</button>
<input type="submit" value="Подтвердить">
<table>
<tr>
<td class='name_1'>ФИО</td>
<td class='subj1_1'>1-ый предмет</td>
<td class='subj2_1'>2-ой предмет</td>
<td class='subj3_1'>3-ий предмет</td>
</tr>
<tr>
<td class='name_2'>text</td>
<td class='subj1_2'>text</td>
<td class='subj2_2'>text</td>
<td class='subj3_2'>text</td>
</tr>
</table>
</form>
<script>
$(document).ready(function() {
$("button").click(function(){
var line = "<tr><td class='name'>Text</td><td class='subj1'></td><td class='subj2'></td><td class='subj3'></td></tr>"
$("table").append(line)
$("tr:last").hide()
$("tr:last").fadeToggle(1000)
});
});
</script>
Get the length of tr using $("#tableId tr).length and add 1 to it
$(document).ready(function() {
$("button").click(function(e) {
e.preventDefault();
let getTRLength = $("#myTable tr").length + 1;
var line = "<tr><td class='name_" + getTRLength + "'>Text</td><td class='subj1'></td><td class='subj2'></td><td class='subj3'></td></tr>"
$("table").append(line)
$("tr:last").hide()
$("tr:last").fadeToggle(1000)
});
});
.name_3 {
color: green;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<form action="" method="post">
<button>Добавить</button>
<input type="submit" value="Подтвердить">
<table id="myTable">
<tr>
<td class='name_1'>ФИО</td>
<td class='subj1_1'>1-ый предмет</td>
<td class='subj2_1'>2-ой предмет</td>
<td class='subj3_1'>3-ий предмет</td>
</tr>
<tr>
<td class='name_2'>text</td>
<td class='subj1_2'>text</td>
<td class='subj2_2'>text</td>
<td class='subj3_2'>text</td>
</tr>
</table>
</form>
Your code doesn't work because you didn't specify the button type attribute. Always specify the type attribute for a <button> element. Different browsers use different default types for the <button> element.
If you use the <button> element in an HTML form, different browsers may submit different values. Use <input> to create buttons in an HTML form.
If your buttons are not to submit form data to a server, be sure to set their type attribute to button. Otherwise they will try to submit form data and to load the nonexistent response, possibly destroying the current state of the document.
$( document ).ready( function() {
$( 'button' ).click( function() {
var line = "<tr><td class='name'>Text</td><td class='subj1'></td><td class='subj2'></td><td class='subj3'></td></tr>";
$( 'table' ).append( line );
$( 'tr:last' ).hide().fadeToggle( 1000 )
} );
} );
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<form action="" method="post">
<button type="button">Добавить</button>
<input type="submit" value="Подтвердить">
<table>
<tr>
<td class='name_1'>ФИО</td>
<td class='subj1_1'>1-ый предмет</td>
<td class='subj2_1'>2-ой предмет</td>
<td class='subj3_1'>3-ий предмет</td>
</tr>
<tr>
<td class='name_2'>text</td>
<td class='subj1_2'>text</td>
<td class='subj2_2'>text</td>
<td class='subj3_2'>text</td>
</tr>
</table>
</form>
I'm trying to get the Knockout foreach binding working in my code. I've done it many times before, but in this particular case I can't seem to get it right. I must be doing something wrong here, but I can't see it. I created a jsfiddle here: https://jsfiddle.net/9Lc144jv/
JavaScript:
var ReportModel = function (parent) {
var self = this;
self.parent = parent;
self.filters = ko.observableArray([]);
self.addFilter = function () {
self.filters.push({ logicOperator: 0, columnName: null, comparisonOperator: 0, value: null });
};
self.removeFilter = function () {
self.filters.remove(this);
};
};
var ViewModel = function () {
var self = this;
self.reportModel = false;
self.init = function () {
self.reportModel = new ReportModel(self);
};
};
var viewModel;
$(document).ready(function () {
viewModel = new ViewModel();
ko.applyBindings(viewModel);
viewModel.init();
});
HTML:
<body>
<table class="table table-bordered">
<thead>
<tr>
<th></th>
<th>Column</th>
<th>Operator</th>
<th>Value</th>
<th></th>
</tr>
</thead>
<tbody>
<!-- ko foreach: reportModel.filters -->
<tr>
<td><input type="text" class="form-control" data_bind="value: logicOperator" /></td>
<td><input type="text" class="form-control" data_bind="value: columnName" /></td>
<td><input type="text" class="form-control" data_bind="value: comparisonOperator" /></td>
<td><input type="text" class="form-control" data_bind="value: value" /></td>
<td>
<button type="button" class="btn btn-danger" data-bind="click: $parent.removeFilter">
Remove
</button>
</td>
</tr>
<!-- /ko -->
</tbody>
<tfoot>
<tr>
<td colspan="5" style="text-align: right;">
<button type="button" class="btn btn-primary" data-bind="click: reportModel.addFilter">
Add
</button>
</td>
</tr>
</tfoot>
</table>
</body>
UPDATE
A few notes:
I was using the wrong attribute: "data_bind" instead of "data-bind". I corrected it now and the updated code is here: https://jsfiddle.net/9Lc144jv/2/
It's still not working even though I made that fix
When I copied and pasted it to a plain .html file and ran it, I could see something interesting in Firebug:
As you can see, running the following script in the command window shows there is nothing in the collection before clicking Add, and then something afterwards:
JSON.stringify(viewModel.reportModel.filters());
So the new object is in the observable array, but it's not binding to the table in the foreach block. But, why? From what I can see, everything looks fine.. but maybe I need a fresh pair of eyes on this. Someone please show me what I am doing wrong here...
You are setting reportModel after the bindings plus I had to add the function call to the buttons() .
slight changes:
var ReportModel = function (parent) {
var self = this;
self.parent = parent;
self.filters = ko.observableArray([]);
self.addFilter = function () {
self.filters.push({ logicOperator: 0, columnName: null, comparisonOperator: 0, value: null });
};
self.removeFilter = function () {
self.filters.remove(this);
};
};
var ViewModel = function () {
var self = this;
self.reportModel = new ReportModel(self);
self.init = function () {
console.info(self);
//self.reportModel = new ReportModel(self);
};
};
var viewModel;
$(document).ready(function () {
viewModel = new ViewModel();
ko.applyBindings(viewModel);
viewModel.init();
});
HTML
<body>
<table class="table table-bordered">
<thead>
<tr>
<th></th>
<th>Column</th>
<th>Operator</th>
<th>Value</th>
<th></th>
</tr>
</thead>
<tbody>
<!-- ko foreach: reportModel.filters -->
<tr>
<td><input type="text" class="form-control" data_bind="value: logicOperator" /></td>
<td><input type="text" class="form-control" data_bind="value: columnName" /></td>
<td><input type="text" class="form-control" data_bind="value: comparisonOperator" /></td>
<td><input type="text" class="form-control" data_bind="value: value" /></td>
<td>
<button type="button" class="btn btn-danger" data-bind="click: $parent.removeFilter()">
Remove
</button>
</td>
</tr>
<!-- /ko -->
</tbody>
<tfoot>
<tr>
<td colspan="5" style="text-align: right;">
<button type="button" class="btn btn-primary" data-bind="click: reportModel.addFilter()">
Add
</button>
</td>
</tr>
</tfoot>
</table>
</body>
https://jsfiddle.net/vw2kngqq/
I am dynamically adding elements. However when I try and serialize the form, none of the dynamically generated elements are serialized.
This is the function I'm using to add elements to the page :
function addObjects(IDname,classname)
{
//to add more objects
var number;
switch(classname)
{
case "name":
number = no_Name++;
break;
case "part":
number = no_part++;
break;
}
var id = classname + number;
$("#"+IDname).append('<tr class="'+id+'"><td><input id="'+id+'" class="'+id+'" type="text"> <button class="'+id+'" onclick=removeAdditions("'+id+'")>x</button></td></tr>');
}
The page looks like this:
<html>
<head>
<script src="Controller.js" type="text/javascript"></script>
<script type="text/javascript" src="https://ajax.microsoft.com/ajax/jQuery/jquery-1.4.2.min.js"></script>
<script type="text/javascript">
//in order to prevent form reload when button click occurs
$(document).ready(function(){
document.getElementById("ReportForm").onsubmit = function (event) { event.preventDefault(); }
});
</script>
</head>
<body>
<div class="detailsPane" id="detailsPane1" >
<form id="ReportForm" name="ReportForm" >
<table style="width: 100%;">
<tbody>
<tr>
<td>
1. Describe the Status briefly-
</td>
<td>
<textarea id="StatDescp" name="StatDescp"></textarea>
</td>
</tr>
</tbody>
</table>
<br>
<table style="width: 100%;">
<thead>
<tr>
<th colspan="4" align="top">
Part Status
</th>
</tr>
</thead>
<tbody>
<tr>
<td style="vertical-align:top;">
Part Name:
</td>
<td style="vertical-align:top;">
<table >
<tbody id="PartName">
<tr class="partname0">
<td><input class="part_name" type="text"> <button onclick='addObjects("PartName","part_name");'>+</button></td>
</tr>
</tbody>
</table>
</td>
<tbody>
</table>
</form>
</div>
<div id="buttonDiv" >
<a class="bottomLeftResultDiv" id="messageBox"></a>
<input type="button" id="saveButton" value="Save" style="width:85px" onclick="save();" />
</div>
</body>
</html>
And finally here is the save Button.
function save() {
var select = document.getElementById('newReportPane');
var contents = $('#ReportForm').serialize();
contents = contents.replace(/=on/g, "=checked");
contents = contents.replace(/\+/g, " ");
$("#messageBox").html("Saving report...");
console.log(contents);
$.post("/Report/Report1", { action: "save", content: contents }, function (data) {
if (data != "ACK")
$("#messageBox").html("Unable to save.");
else
$("#messageBox").html("Report saved successfully");
});
}
When I click on the save button, it only posts this StatDescp= without any of the dynamically generated elements.
I really can't figure out why.
Any help would be appreciated.
Give a name= attribute to each of your added inputs.
From http://api.jquery.com/serialize/
For a form element's value to be included in the serialized string,
the element must have a name attribute.
I have a table in a cell that displays the numbers a user enters with buttons (using onclick and a showthis function. I need to be able to store the value as a variable in order to perform operations on it. How can I do this?
PS: I am using JavaScript and HTML
<script language="JavaScript" type="text/javascript">
function showthis(first){
document.getElementById("displaycell").innerHTML+=first;
}
</script>
<body>
<h1 align="center"> RPN Calculator </h1>
<table summary align="center" border="1" cellpadding="1" cellspacing="3">
<tr>
<th id="displaycell" colspan="5" type="text"> </td>
</tr>
<tr>
<td>
<button type="button" onclick="showthis('1')">1</button>
</td>
<td>
<button type="button" onclick="showthis('2')">2</button>
</td>
<td>
<button type="button" onclick="showthis('3')">3</button>
</td>
<!-- ... -->
Firstly you should not be using += with innerHTML. It means that you will end up with the numbers appending to the cell's internal value rather than overwriting it.
function showthis ( number ) {
var cell = document.getElementById('displaycell');
cell.innerHTML = "";
cell.appendChild( document.createTextNode( number ));
}
Would be a much better way to handle that.
Next, within showthis you are best off storing the value in a variable so that you can access it directly from javascript in the future.
var displayStore = 0;
function showthis ( number ) {
var cell = document.getElementById('displaycell');
cell.innerHTML = "";
cell.appendChild( document.createTextNode( number ));
// you can either do this in a variable local to the <td> DOM Object like so
cell.currentDisplayNumber = number;
//or in a global variable like so
displayStore = number;
}
Finally, to access that variable again you can either read it out of the displaystore <td> or read it from your variable.
function DoStuff0 () {
var number = Number( document.getElementById( 'displaycell' )).innerHTML;
// rest
}
function DoStuff1 () {
var number = document.getElementById('displaycell').currentDisplayNumber;
// rest
}
function DoStuff2 () {
var number = displayStore;
// rest
}
Is this what you wanted? http://jsfiddle.net/CWQY2/
HTML:
<h1 align="center"> RPN Calculator </h1>
<table summary align="center" border="1" cellpadding="1" cellspacing="3">
<tr>
<th id="displaycell" colspan="5"> </th>
</tr>
<tr>
<td> <button type="button" onClick="showthis('1')">1</button> </td>
<td> <button type="button" onClick="showthis('2')">2</button> </td>
<td> <button type="button" onClick="showthis('3')">3</button> </td>
</tr>
</table>
JS:
function showthis(first){
document.getElementById("displaycell").innerHTML += first;
}