I am trying to learn some HTML and JavaScript (from mostly using C#) and I got a test from a friend in making a small webstore.
I have some checkboxes for the different items, and the goal is if I combine different products, I get a discount. However there are two items with the same value that need different combinations for discounts. But the challange is that I am NOT ALLOWED to edit the html file.
Is there a way to tell these items apart with JavaScript? The only thing differing is the <h2>.
Again, I am very new to JavaScript, but I do have some knowledge of scripting, so if you got an solution, please do comment on what the function does so I can learn it better to implement in the code.
HTML and code:
Note that this is not the whole code, this is the only parts that I think is connected to each other for this question. If full script is wished for, I will make a pastebin for it.
$('body').on('click', '.fruit, .stuff1, .stuff2, .stuff3', update);
function update(){
let fruit = $('.fruit>input:checked').val();
fruit = parseInt(fruit);
fruit = fruit? fruit : 0;
let price = fruit + stuff1 + stuff2 + stuff3;
//If combined, get discount
//This is for the banana
//This should only apply for banana
if(fruit == 200 && stuff1 == 100)
{
price = price * 0.8;
$('#confirm').children('h2').text("You get 20% discount");
}
//This is an second discount in a else if ladder
//This is for apple
//This should only apply for apple
else if(fruit == 200 && stuff1 == 150)
{
price = price * 0.9;
$('#confirm').children('h2').text("You get 10% discount");
}
else
{
$('#confirm').children('h2').text("You dident get anny discount");
}
$('#price').val(price);
display(price);
}
<div class="picker">
<div class="fruit">
<section>
<h2>Banana</h2>
</section>
<input type="radio" name="fruit" value="200">
</div>
<div class="fruit">
<section>
<h2>Apple</h2>
</section>
<input type="radio" name="fruit" value="200">
</div>
</div>
When you use document.getElementsByName("fruit") in JavaScript it returns an array; so, document.getElementsByName("fruit")[0] returns the first radio and document.getElementsByName("fruit")[1] returns the second radio.
That said, you are using jQuery which is a JavaScript Framework. The syntax in jQuery to do the same thing is the slightly shorter $('[name="fruit"]').
Related
I am learning js, recently, I apologize for my English and for the obviousness of my questions. I have two problems, I would appreciate solutions in pure JavaScript.
Following several tutorials, I installed a localStorage script in an HTML page.
pag1.html
<p> Hello! what's your name?</p>
<form action="pag2.html">
<input type="text" id="txt"/>
<input type="submit" value="nome" onClick="passvalues();"/>
</form>
// the form has a link to pag2.html
<script>
function passvalues()
{
var nme=document.getElementById("txt").value;
localStorage.setItem("textvalue",nme);
return false;
}
</script>
Let's say the user enters the name "Lucy":
pag2.html
<p>Hi <span id="result">Lucy<span> nice to meet you!</p>` // the name appears
<p>How are you today <span id="result">NOTHING</span>?</p>` // the name does not appear
pag3
<script>
document.getElementById("result").innerHTML = localStorage.getItem("textvalue");
</script>
First Question
the NAME appears only once for my mistake or must it be so? Should I use another syntax to make it appear several times?
pag3.html
<p><span id="result">Lucy</span>which gender designation do you prefer to be used with you?</p>
<p>male</p>
<p>female</p>
<p>neutral</p>
<script>
document.getElementById("result").innerHTML = localStorage.getItem("textvalue");
</script>
Second Question
In pag 3 the user has to choose whether he wants to be called male, female or neutral.
One solution is to set three different pages according to the genre chosen. But this solution is not elegant and inflates the number of pages.
I seem to have read somewhere that with js it is possible to go to another page and at the same time set the value of a Boolean variable (true / false) through a link.
Something like this (the syntax is invented):
<a href="female.html" set var f true> female</a>
This would allow you to create a single landing page with the insertion of three "if":
if (female true) {
text = "brava";
} else if (male true) {
text= "bravo";
} else {
text = "bene";
}
Is it possible to do this? How?
I have tried this script htmlgoodies several times but it is too difficult for me.
Hello,
First of all I'd thank #cssyphus and #Shahnawazh for the answers.
Question 1
I managed to show the name several times on the same page with the script:
<script>
var result = document.getElementsByClassName('result');
[].slice.call(result).forEach(function (className) {
className.innerHTML = localStorage.getItem("textvalue");
});
</script>
But, do I really need to include it on all pages that need to show the name?
I also tried to insert the script into an external js page but the name did not appear even once.
Question 2
As for the second question, I did not get positive results.
Rather than using id, you can use class for showing result because id is unique. Also for storing male, female or neutral you can use radio buttons, and when you click any of them, just save the result in the localStorage. Here are the three pages.
page1.html
<body>
<p> Hello! what's your name?</p>
<form action="page2.html">
<input type="text" id="txt" />
<input type="submit" value="name" onClick="passvalues();" />
</form>
<script>
function passvalues() {
var nme = document.getElementById("txt").value;
localStorage.setItem("textvalue", nme);
return false;
}
</script>
</body>
page2.html
<body>
enter code here
<p>Hi <span class="result">Lucy<span> nice to meet you!</p>
<p>How are you today <span class="result"></span>?</p>
page3
<script>
var result = document.getElementsByClassName('result');
[].slice.call(result).forEach(function (className) {
className.innerHTML = localStorage.getItem("textvalue");
});
</script>
</body>
page3.html
<body>
<p><span class="result"></span> which gender designation do you prefer to be used with you?</p>
<form name="genderForm" action="">
<input type="radio" name="gender" value="male"> Male<br>
<input type="radio" name="gender" value="female"> Female<br>
<input type="radio" name="gender" value="neutral"> Neutral
</form>
<p>You've selected <span class="selectedGender"></span></p>
<script>
var result = document.getElementsByClassName('result');
[].slice.call(result).forEach(function (className) {
className.innerHTML = localStorage.getItem("textvalue");
});
var rad = document.genderForm.gender;
var prev = null;
for (var i = 0; i < rad.length; i++) {
rad[i].addEventListener('change', function () {
(prev) ? console.log(prev.value) : null;
if (this !== prev) {
prev = this;
}
console.log(this.value);
document.getElementsByClassName("selectedGender")[0].innerHTML = this.value;
localStorage.setItem("gender", this.value);
});
}
</script>
</body>
You have just discovered why you cannot have two elements on the same page with the same ID - only the first one will work. However, classes work almost exactly like IDs, so just use the same className at both locations:
<p>Hi <span class="result">Lucy<span> ...
...
<p>How are you today <span class="result">Lucy</span> ...
As to your second problem, just use localStorage again. Set a different localStorage variable and read/write that.
However, what you are probably thinking of is using a query string, like this:
female
See this SO question for information and code examples.
#JonoJames answered this question for me.
The HTML form we use has a set of checkboxes which are checked depending on which room is being hired. These boxes are named room1, room2 and room3. When one, two or all three boxes are checked, the cost of that rooms hire is put into corresponding, hidden, input boxes named room1value, room2value and room3value.
We later needed to add deposits for the room hire which we did by adding, for example, document.forms["bookform"].room1deposit.value = Rooms[0].deposit; to the function check() script for each of the rooms. This added the deposit for the room automatically and it worked fine.
However now, occasionally, a deposit may not be required, so we put a checkbox in the form that is checked when a deposit is required. This checkbox, which is called depositRequired, covers all three rooms. So if someone hires Room 1 and a deposit is required, boxes room1 and depositRequired are checked. Similarly, If the boxes for Room 2 and Room 3 are checked and a deposit is required, the depositRequired checkbox is selected too. If a room or rooms are hired and a deposit is not required, the required room boxes are checked but the depositRequired checkbox remains unselected.
We've tried ((document.forms["bookform"].room1value.checked) || (document.forms["bookform"].room1deposit.checked)), ((document.forms["bookform"].room1deposit.checked) && (document.forms["bookform"].room1value.checked)) to the if statements and we've tried adding and else if to the statements, separate if statements and variations of the above but we can't get anything to work.
Our current code is:
function Check() {
//room1
if (document.forms["bookform"].room1.checked)
{
document.forms["bookform"].room1value.value = Rooms[0].cost;
document.forms["bookform"].room1deposit.value = Rooms[0].deposit;
//console.log(Rooms[0].cost);
}else{
document.forms["bookform"].room1value.value ='';
document.forms["bookform"].room1deposit.value = '';
}
//room2
if (document.forms["bookform"].room2.checked)
{
document.forms["bookform"].room2value.value = Rooms[1].cost;
document.forms["bookform"].room2deposit.value = Rooms[1].deposit;
//console.log(Rooms[1].cost);
}else{
document.forms["bookform"].room2value.value ='';
document.forms["bookform"].room2deposit.value = '';
}
//room3
if (document.forms["bookform"].room3.checked)
{
document.forms["bookform"].room3value.value = Rooms[2].cost;
document.forms["bookform"].room3deposit.value = Rooms[2].deposit;
//console.log(Rooms[2].cost);
}else{
document.forms["bookform"].room3value.value ='';
document.forms["bookform"].room3deposit.value = '';
}
}
and the output boxes are:
<input type="hidden" name="room1value" readonly>
<input type="hidden" name="room1deposit" readonly>
<input type="hidden" name="room2value" readonly>
<input type="hidden" name="room2deposit" readonly>
<input type="hidden" name="room3value" readonly>
<input type="hidden" name="room3deposit" readonly>
How do we add the deposit if the deposit checkbox is checked and the specific room is checked?
For example, if Room 1 is checked the cost of the room is output to the room1value input box but the deposit cost for Room1 is output to the input box room1deposit only if the deposit required checkbox is checked else it remains blank.
In your attempts you have tried using room1value.checked and room1deposit.checked, but those are not check boxes, but the hidden inputs in which you want to write the amounts. Instead you should use depositRequired.checked.
So you would need a nested if, like this:
if (document.forms["bookform"].room1.checked) {
document.forms["bookform"].room1value.value = Rooms[0].cost;
if (document.forms["bookform"].depositRequired.checked) { // <--- Additional IF
document.forms["bookform"].room1deposit.value = Rooms[0].deposit;
}
} // ... etc
I would however also point out that you can shorten your code a bit, as it currently has a lot of repetition.
Secondly, it would be good if you would use a lower case first letter for your function check and array rooms, as an initial capital is commonly used for class names (constructors).
Here is how you could deal with all three groups in a single loop:
function check() { // <--- initial char lower case
let frm = document.forms["bookform"]; // avoid reading the document repetitively
for (let i = 1; i <= 3; i++) {
// Use dynamic properties all over...
frm[`room${i}value`].value = ""; // default
frm[`room${i}deposit`].value = ""; // default
if (frm[`room${i}`].checked) {
frm[`room${i}value`].value = rooms[i].cost; // lowercase
if (frm.depositRequired.checked) { // <--- Additional IF
frm[`room${i}deposit`].value = rooms[i].deposit; // lowercase
}
}
}
}
Back again! I always get such great responses here!
I currently have a HTML site with multiple onclick buttons calling up a js function allowing users to "opt-in" to certain notification services. If the user wants to op-in to multiple groups, they are required to hit each button one at a time.
It technically works, but as you can imagine it certainly doesn't look pretty. Additionally, the way it's set up now, the user doesn't get any feedback that they have successfully subscribed to said group (not a huge concern though).
Here's a snip of the code right now:
<button onclick="_pcq.push(['addSubscriberToSegment', '50cc']);">
50cc
</button>
<button onclick="_pcq.push(['addSubscriberToSegment', '65a']);">
65A
</button>
<button onclick="_pcq.push(['addSubscriberToSegment', '65b']);">
65B
</button>
<button onclick="_pcq.push(['addSubscriberToSegment', '85a']);">
85A
</button>
<button onclick="_pcq.push(['addSubscriberToSegment', '85b']);">
85B
</button>
I should also note that although I only listed 5 buttons here, the website actually has more than 20...which is pretty overwhelming.
Does anyone have an elegant way (open to suggestions) to format this?
I thought of doing bullets, but I struggled with getting the form to run 'addSubscriberToSegment' with different values off of one submit button.
Surely it's just my lack of expertise that has got me stumped. Perhaps some of you will have a nicer solution for me?
Thanks!
I would do nested checkboxes for something like this. Each option has a checkbox, but there are also general sections that you can check on or off to enable/disable a group of options. Take a look at this codepen by lonekorean for an idea how to style them, you can add a little bit of javascript to make parent checkboxes enable/disable their children items.
The best way, probably, is to replace your buttons with checkboxes, as mentioned by u_mulder.
In any case it is better to create a function where you can handle the subscription and the notification instead of pushing things on the fly into an array.
for example:
function subscribeAnything(codeVariable){
_pcq.push(codeVariable);
alert("Great you have subscribed to " + codeVariable);
}
Then you add an eventlistener as commented by Jordan Davis to link this function.
If you decide to effectively replace the buttons by checkboxes, you could set the subscription code into the name or id of each checkbox and then loop trough your checkboxes to push them into your array.
var myChbxArray = [];
var btn = document.getElementById("pushCheckBoxes");
btn.addEventListener("click", pushAllChbxIntoArray, false);
function pushAllChbxIntoArray(){
myChbxArray = []; // clear content of array
var inputs = document.getElementsByTagName("input");
for(var i = 0; i < inputs.length; i++) {
if(inputs[i].type == "checkbox" && inputs[i].checked == true) {
myChbxArray.push(inputs[i].name);
}
}
notification();
}
function notification(){
var myDiv = document.getElementById("result");
myDiv.innerHTML = "You have checked the following checkboxes:<br>";
for(var i = 0; i < myChbxArray.length; i++) {
myDiv.innerHTML = myDiv.innerHTML + myChbxArray[i] + "<br>";
}
}
ul{
list-style-type: none;
}
<div id="checkboxList">
<ul>
<li><label for="chk1"><input type="checkbox" name="chk1" id="chk1">Chk1</label></li>
<li><label for="chk2"><input type="checkbox" name="chk2" id="chk2">Chk2</label></li>
<li><label for="chk3"><input type="checkbox" name="chk3" id="chk3">Chk3</label></li>
<li><label for="chk4"><input type="checkbox" name="chk4" id="chk4">Chk4</label></li>
</ul>
<button id="pushCheckBoxes">ClickMe</button>
</div>
<div id="result">
</div>
I have a table named, student_db which is like this :-
Name Gender Grade City
John Male 2 North
Dave Male 4 North
Garry Male 3 North
Chirsty Female 5 East
Monica Female 4 East
Andrew Male 3 East
Patrick Male 7 South
Maria Female 3 South
I need to select 3 students with this list, names of the students are taken as input. But there are some constraints :
1) 1 Female has to be selected.
2) A maximum of 2 persons from the same city can be selected.
3) Sum of the grade of the selected 3 persons cannot exceed 11.
4) Once three valid selections are made, rest of the checkboxes should freeze automatically.
5) If while selecting a person any of the constraint gets violated, that particular selections gets unchecked and a alert message is displayed to the user.
Is it possible to add so many constraints to a check box ??
EDIT
I have managed to add 2 constraints :-
1) If the grade sum exceeds 11, an alert message will get displayed.
2) Once three valid selections are made, rest checkboxes will get freezed.
This is what I've tried :-
<script src="http://code.jquery.com/jquery-1.9.1.js" type="text/javascript"></script>
<script type="text/javascript">
jQuery(function() {
var max = 3;
var checkboxes = $('input[type="checkbox"]');
checkboxes.change(function() {
var current = checkboxes.filter(':checked').length;
checkboxes.filter(':not(:checked)').prop('disabled', current >= max);
});
});
</script>
<script>
function jsFunction(element) {
var sum = 0,i;
var elements = document.getElementsByClassName('chkbox');
for (i = 0; i<elements.length; i++) {
if (elements[i].checked) {
sum += parseInt(elements[i].value);
}
}
if (sum > 11) {
alert("11 maximum grade allowed !!");
element.checked = false;
}
}
</script>
<form name='checkbox1[]' method="post" action="select_student.php">
<label class="cb1" for="checkbox1"> </label>
<input type="hidden" name="checkbox1[]" id="check" value="null">
<?php
session_start();
mysql_connect("localhost", "my_db", "my_password") or die (mysql_error ());
mysql_select_db("my_db") or die(mysql_error());
$strSQL = "SELECT Name,Grade FROM student_db";
$rs = mysql_query($strSQL);
echo "<b><h2>List of Students :-</h2></b>";
while($row = mysql_fetch_array($rs)) {
$man = $row['Name'];
echo '<input type="checkbox" value="'.$row['Name'].'|'.$row['Grade'].'" class="chkbox" name="checkbox1[]" onchange="jsFunction(this)" />';
echo $man;
}
?>
<br>
<input type="submit" value="SUBMIT" id="button1" style= "height:40px; width:150px; font-weight: bold; font-size:10;">
</form>
The JS function is used to check whether the Grade sum is exceeding 11 or not and the jQuery Functions freezes other boxes once 3 valid selections are made. But I am unable to add other constraints.
Sorry for the delayed response! I got caught up in some other stuff yesterday and had to pick back up this morning . . . hopefully, this will help you out.
What you have is a pretty good start . . . here's a few changes that I would suggest:
1) First off, some HTML changes:
store off all of your student data (i.e., the "gender", "grade", and "city" values) as data attributes in the checkbox, like this:
<input type="checkbox" value="NAME_VALUE" class="chkbox" name="checkbox1[]"
data-gender="GENDER_VALUE" data-grade="GRADE_VALUE" data-city="CITY_VALUE"
onchange="jsFunction(this)" />
next, since you are already using jQuery, for clarity and easy of maintenance in the future, apply the onchange event listener dynamically, rather than hardcoding it into the input, like this:
<input type="checkbox" value="NAME_VALUE" class="chkbox" name="checkbox1[]"
data-gender="GENDER_VALUE" data-grade="GRADE_VALUE" data-city="CITY_VALUE" />
. . . and, in the script, after the page load:
$(".chkbox").on("change", jsFunction);
And, finally (just as a heads up), if you intend to us a <label> tag with a for attribute (e.g., you are showing one for your "hidden" input), you will need a matching id attribute in yout <input>, in order for them to be paired.
2) As for your scripting, I threw together the following that accomplishes all of the validation checks that you were looking for and standardizes some of your coding (you had a heavily mixed use of jQuery and vanilla JS).
var MAX_CHECKED = 3;
var MAX_GRADE = 11;
$("document").ready(function() {
$(".chkbox").on("change", jsFunction);
});
function jsFunction() {
var sum = 0;
var cities = [];
var elements = $(".chkbox");
var checkedElements = elements.filter(":checked");
checkedElements.each(function() {
sum += parseInt($(this).data("grade"), 10);
cities.push($(this).data("city"));
});
var uniqueCities = cities.filter(function(currentCity, cityIndex, cityArray) {
return (cityIndex === cityArray.indexOf(currentCity));
});
if (sum > MAX_GRADE ) {
alert(MAX_GRADE + " maximum grade allowed!!");
$(this).prop("checked", false);
}
else if (uniqueCities.length !== cities.length) {
alert("You may not select more than one student from each city!!");
$(this).prop("checked", false);
}
else {
if (checkedElements.length >= MAX_CHECKED) {
if (checkedElements.filter("[data-gender='Female']").length < 1) {
alert("At least one female student must be selected!!");
$(this).prop("checked", false);
}
else {
elements.filter(':not(:checked)').prop('disabled', true);
}
}
else {
elements.filter(':not(:checked)').prop('disabled', false);
}
}
}
Most of the code should be pretty straightforward . . . let me touch on a couple of the key or more complex parts . . .
Globals - I set your global variables (MAX_CHECKED and MAX_GRADE) up at the top and outside of any functions, so that they are accessible anywhere and easy to update (i.e., you don't have to search through the code to find them).
"Setup code" - I moved your setup code from the jQuery(function() { that you used to $("document").ready(function() { simply for readablity. :) They do the same thing.
Event binding the checkboxes - as I mentioned, I did this dynamically rather than inline, using $(".chkbox").on("change", jsFunction);. Additionally, you'll notice that I changed the selector. Since each checkbox is tagged with the "chkbox" class, I selected based on that, it is significantly faster than $('input[type="checkbox"]')
The validation logic - basically, you want to catch invalid entries as soon as possible and you have rules that fall into two categories: rules that can be checked before the maximum number of selections have been met and rules that require the maximum. The "max grade" and "unique cities" checks should be caught as soon as they are hit, but you must wait until you've hit the maximum selections before you can check for gender, since the user can always check a female student later on, if they have not yet hit the max.
uniqueCities - the function used to create this value isn't quite as straighforward as the rest of the code, so I'll clarify it a little. It is using the .filter method that is native to Arrays, to trim down the selections to only unique values. It does this by doing a callback which checks to see if the index of the current item (i.e., cityIndex) is the same as the index of the first instance of that value (i.e., currentCity) in the array (i.e., cityArray). If it is, then that value is added to the "filtered" array (i.e., uniqueCities).
"undisabling" - I added in an else statement that re-enables the checkboxes if the maximum number of selections have been made, but then one of them is unchecked. This will allow the user to change their mind, even if they had already hit the max, at some point.
A couple of extra notes
1) You need to change the value of Patrick's to "6" at the most, in order to check that the "at least one female" logic is working . . . currently, the lowest combination of all male students, from unique cities, is John, Garry, and Patrick, but their grade total is 12, so it triggers the "grade sum is too high" validation first. :)
2) While I didn't cover the code for this, you need to develop a version of this validation logic for your "Submit" button as well. If you don't, then someone could select two boys and hit "Submit" and the "at least one female" logic would never be triggered.
One way would be to put the "at least one female" validation in its own method and calling both from the "I just clicked on a checkbox" validation and in a "I just clicked on the submit button" validation. The main difference would be that, for the submit, you would not check to see if the max number of selections had been made, before triggering the "female" check. That would also be a good place to add an "at least one selection has been made" validation, as well.
I have a table of items available for purchases which I am displaying on the site. I am using mysql to fetch all the items and display them in a table. Among others, the table contains this:
<input type="hidden" name="price" id="price'.$id.'"> //id is one part of MySQL query results
<input type="text" name="count_'.$id.'">
All this is displayed for around 200 items with ID being not completely in sequence (I found some JavaScript code that used for (i = 0; i < rows.length; i++) {}, however, with my IDs not being in a sequence, this is not a good option for me).
I would like to display a total of an order using JavaScript and I am not experienced when it comes to JS. I would be very thankful for your advices.
You coul duse jQuery:
function orderTotal()
{
var total=0;
$('input[name="price"]').each(function(){
var price = parseFloat($(this).val());
var amount = parseFloat($('input[name="count_'+$(this).attr('name').substring(5)+'"]').val());
total += price+amount;
});
return total;
}
Consider adding a class to each element that you want to count and see the answer below on stackoverflow. You should be able to have a counter for each occurrence of the class and show this variable in the html
How to getElementByClass instead of GetElementById with Javascript?
<div class="item"> ... <your inputs> ... </div>
I suggest you wrap them in another element, lets use div. Add a class to that, lets say moneyline
<div class="moneyline">
<input class="price" type="hidden" name="price" id="price'.$id.'"> //id is one part of MySQL query results
<input class="quantity" type="text" name="count_'.$id.'">
</div>
Im going to give the example with jQuery, and some button to trigger it:
$('#someButton').on('click', function(){
var total = 0;
$('.moneyline').each(function(){
var price = parseInt($(this).find('.price'), 10);
var quantity = parseInt($(this).find('.quantity'), 10);
total+= quantity*price;
});
alert( total );
});