Same behavior apply to different elements - javascript

For div.spot1 and div.spot2 I want the same behaviors - hide delete button when image input is empty, show delete btn when image is uploaded, change image src when image is uploaded, click delete btn to send ajax request to server etc. Of course I can copy/paste js code of spot1 to spot2, only changing the class from first to second. But say I have 4 spots like this, then I will have four almost identical code in my js file. I fear that's not the best practice. What should I do?
HTML:
<form class='upload' action="" method="POST" enctype="multipart/form-data">
<div class="spot1">
<span class='filename first'>FILE 1</span>
<button type='button' class='delete first'>&#x274C</button>
<input type="file" name="image" class="input first" accept="image/*">
<input type="button" value="+" class="browse first"/>
<img class="tn first" src="{{turl}}">
<span class="status first"></span>
</div>
<div class="spot2">
<span class='filename second'>FILE 1</span>
<button type='button' class='delete second'>&#x274C</button>
<input type="file" name="image" class="input second" accept="image/*">
<input class="browse second" type="button" value="+"/>
<img class="tn second" src="{{turl}}">
<span class="status second"></span>
</div>
</form>
Part of the JS (just for making an example):
if ( $('.tn.first').attr('src') == "") {
...
}
$('.browse.first').on("click", function () {
$('.input.first').click();
});
$('.input.first').on('change', function () {
...
};

That is indeed not best practice.
If you know what kind of structure you will have (exactly), you can do something like this:
var FileModule = function(elem) {
// Throw your events here
}
Instantiating it is as simple as passing your elements in it. If you are using jQuery, you can do this exactly using the $.fn.extend() function, as follows:
$.fn.FileModule = function() {
return this.each(function() {
// `this` is your element
$(this).find(".input.first").on("change", function() { ... });
});
};
From there, you can initialize your module (that is what it is) by calling FileModule on a jQuery selector that contains your spots.
If you want even more dynamic, consider binding custom events on it in order to allow external portions of your code to interact with it. This is beyond the scope of your question, hozever.

If you have multiple elements of the same type, that represent the same type of data and/or functionality, they should have the same class name. You can add additional attributes to your elements that tell you which one of that class it is, like this:
<div class="spot" index="1">
<span class='filename'>FILE 1</span>
<button type='button' class='delete'>&#x274C</button>
<input type="file" name="image" class="input" accept="image/*">
<input type="button" value="+" class="browse"/>
<img class="tn" src="{{turl}}">
<span class="status"></span>
</div>
This lets you now write:
$('.browse').on("click", function () {
$(this).find('.input').click();
});

How about change markup following way
<form class='upload' action="" method="POST" enctype="multipart/form-data">
<div class="spot" id="spot1">
<span class='filename'>FILE 1</span>
<button type='button' class='delete'>&#x274C</button>
<input type="file" name="image" class="input" accept="image/*">
<input type="button" value="+" class="browse"/>
<img class="tn" src="{{turl}}">
<span class="status"></span>
</div>
<div class="spot" id="spot2">
<span class='filename'>FILE 2</span>
<button type='button' class='delete'>&#x274C</button>
<input type="file" name="image" class="input" accept="image/*">
<input class="browse" type="button" value="+"/>
<img class="tn" src="{{turl}}">
<span class="status"></span>
</div>
</form>
And JS
$('.spot .browse').on('click', function () {
$(this).parent().find('.input').click();
});
$('.spot .input').on('change', function () {
alert('I\'m input of type file of ' + $(this).parent().prop('id') + ' and I\'m changed');
});
Here is jsFiddle demo

Since you're already using jquery there is a selector which selects based on a substring within in an attribute (attribute contains).
$('div[class*="spot"]').doSomething();

Related

Adding elements one after another using JQuery

I am trying to add multiple div tags within a form using JQuery:
Below is my initial HTML form:
<form action="" method="post">
<div id="div_file0">
<input type="file" name="files0[]" id="files0" multiple><br/>
</div>
<a href="#" id='more_files'>Click to add more files</a>
After uploading multiple files, click Submit.<br>
<input type="submit" value="Submit">
</form>
Upon clicking on Click to add more files, I want more div tags to be created as below:
<form action="http://localhost:5000/api/upload_file" method="post">
<div id="div_file0">
<input type="file" name="files0[]" id="files0" multiple=""><br>
</div>
<div id="div_file1">
<input type="file" multiple="" id="files1" name="files1[]"><a class="remove" href="#" id="remove_file" name="remove_file1" value="1">Remove file</a>
</div>
Click to add more files
After uploading multiple files, click Submit.<br>
<input type="submit" value="Submit">
</form>
However, the new div tag replaces the existing content, and adds the new and old input tags within newly created div element.
<form action="http://localhost:5000/api/upload_file" method="post">
<div id="div_file1">
<input type="file" name="files0[]" id="files0" multiple=""><br>
<input type="file" multiple="" id="files1" name="files1[]"><a class="remove" href="#" id="remove_file" name="remove_file1" value="1">Remove file</a>
</div>
Click to add more files
After uploading multiple files, click Submit.<br>
<input type="submit" value="Submit">
</form>
Javascript used is as below:
<script type="text/javascript">
$(document).ready(function() {
$(document).on('click','#more_files', function() {
var numOfInputs = 1;
while($('#files'+numOfInputs).length) { numOfInputs++; }//once this loop breaks, numOfInputs is greater than the # of browse buttons
console.log("numOfInputs:"+numOfInputs)
$("<br/>").insertAfter("#div_file"+(numOfInputs-1));
$("div")
.attr("id","div_file"+numOfInputs)
.insertAfter("#div_file"+(numOfInputs-1));
var input = $("<input type='file' multiple/>")
.attr("id", "files"+numOfInputs)
.attr("name", "files"+numOfInputs+"[]");
var remove = $("<a class='remove' href='#'>Remove file</a>")
.attr("id","remove_file")
.attr("name","remove_file"+numOfInputs)
.attr("value",numOfInputs);
$("#div_file"+numOfInputs).append(input,remove);
});
});
</script>
This rather redundant as you're using a multiple file input already.
If you really want to do it in this manner, then remove all id attributes from the HTML content you're going to repeat in the DOM. They aren't necessary and just create more needless complication. Then you can grab the first .div_file element, clone it, and insert it before the a in the form, like this:
$(document).ready(function() {
$(document).on('click', '#more_files', function() {
var $clone = $('.div_file:first').clone();
$clone.insertBefore('form a');
});
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<form action="" method="post">
<div class="div_file">
<input type="file" name="files[]" multiple><br/>
</div>
<a href="#" id='more_files'>Click to add more files</a> After uploading multiple files, click Submit.<br>
<input type="submit" value="Submit">
</form>

How to save data in localstorage and access it on another html page

I am currently working on a laundry platform and right now I have 2 HTML page which is booking page and summary page. The booking page is where users can book the number of clothes to be washed using the plus and minus button, this would automatically calculate the total amount to be paid. Now I also want the summary page to display such information as the booking page. This actually mean the information would be stored in localStorage by booking page in order to be accessed by summary page and displayed to users.
<body>
<div class="second-booking-container">
<div>
<div class="second-booking-container-image"><img src="./img/shirt.png" /></div>
<p class="second-booking-container-icon" name="product" value="100" id="qnty_1">
Shirt(s)</p>
<p>
<button type="button" class="sub" data- target="shirt">-</button>
<input type="text" value="0" class="field_shirt" />
<button type="button" class="add" data-target="shirt">+
</button>
<p class="display_shirt" name="price" max="3" min="1">₦
100</p>
</p>
</div>
<div>
<div class="second-booking-container-image"><img src="./img/trouser.png" /></div>
<p class="second-booking-container-icon" name="product" value="100" id="qnty_2">
Trouser(s)</p>
<div>
<p>
<button type="button" class="sub" data target="trousers">−</button>
<input type="text" value="0" class="field_trousers" />
<button type="button" class="add" data-target="trousers">+
</button>
<p class="display_trousers" name="price" max="3" min="1">₦
0</p>
</p>
</div>
</div>
<div>
<div class="second-booking-container-image"><img src="./img/skirt.png" /></div>
<p class="second-booking-container-icon" name="product" value="100" id="qnty_3">
Skirt(s)</p>
<div>
<p>
<button type="button" class="sub" data- target="skirts">−</button>
<input type="text" value="0" class="field_skirts" />
<button type="button" class="add" data- target="skirts">+</button>
<p class="display_skirts" name="price" max="3" min="1">₦ 0</p>
</p>
</div>
</div>
<div>
<div class="second-booking-container-image"><img src="./img/blouse.png" /></div>
<p class="second-booking-container-icon" name="product" value="100" id="qnty_4">
Blouse(s)</p>
<div>
<p>
<button type="button" class="sub" data-target="blouses">-
</button>
<input type="text" value="0" class="field_blouses" />
<button type="button" class="add" data-target="blouses">+
</button>
<p class="display_blouses" name="price" max="3" min="1">₦
100</p>
</p>
</div>
</div>
<div>
<div class="second-booking-container-image"><img src="./img/jacket.png" /></div>
<p class="second-booking-container-icon-long" name="product" value="100" id="qnty_5">Suit/Jacket(s)
</p>
<p>
<button type="button" class="sub" data- target="suits">-</button>
<input type="text" value="0" class="field_suits" />
<button type="button" class="add" data-target="suits">+
</button>
<p class="display_suits" name="price" max="3" min="1">₦ 100</p>
</p>
</div>
</div>
<div class="third-booking-container">
<p>Total:₦ <span id="totalValue"></span></p>
<button>Set pick up date
<FontAwesomeIcon class="second-container-button-right" icon="angle-right" /></button>
</div>
</div>
Here is the JS code
var subElm = document.querySelectorAll('.sub');
var addElm = document.querySelectorAll('.add');
var totalValueElm = document.getElementById('totalValue');
for (var i = 0; i < subElm.length; i++) {
subElm[i].addEventListener('click', function () {
var targetItem = this.getAttribute('data-target');
var inputElm = document.querySelector('.field_' + targetItem);
var displayElm = document.querySelector('.display_' +
targetItem);
var currentValue = +inputElm.getAttribute('value');
if (currentValue !== 0) {
var incValue = currentValue - 1;
var strValue = ' ' + incValue;
inputElm.setAttribute('value', incValue);
displayElm.innerHTML = "₦" + strValue;
totalValueElm.innerText = Number(totalValueElm.innerText) -
100;
}
});
addElm[i].addEventListener('click', function () {
var targetItem = this.getAttribute('data-target');
var inputElm = document.querySelector('.field_' + targetItem);
var displayElm = document.querySelector('.display_' +
targetItem);
var currentValue = +inputElm.getAttribute('value');
var incValue = currentValue + 1;
var strValue = ' ' + incValue;
inputElm.setAttribute('value', incValue);
displayElm.innerHTML = "₦" + strValue;
totalValueElm.innerText = Number(totalValueElm.innerText) +
100;
});
}
Here is the second html page that I want to also show same information from the previous booking page
<body>
<div class="summaryContainer">
<div class="summaryNavBar">
<p className="summaryTitle">Summary</p>
</div>
<div class="summaryContent">
<p class="total" id="total">Total:</p>
<p class="sum">₦0.00</p>
</div>
<div class="summaryCard">
<div class="summary-card-title">
<div>Item</div>
<div>Quantity</div>
</div>
<div class="summary-card-content">
<div>Shirt(s)</div>
<div id="
first" class="summary-quantity"><button type="button" id="sub" class="sub">−</button>
<input type="text" id="1" value="0" class="field" />
<button type="button" id="add" class="add">+</button> </div>
</div>
<div class="summary-card-content">
<div>Trouser(s)</div>
<div class="summary-quantity" id="second">
<button type="button" id="sub" class="sub">−</button>
<input type="text" id="1" value="0" class="field" />
<button type="button" id="add" class="add">+</button>
</div>
</div>
<div class="summary-card-content" id="third">
<div>Suit(s)</div>
<div class="summary-quantity"><button type="button" id="sub" class="sub">−</button>
<input type="text" id="1" value="0" class="field" />
<button type="button" id="add" class="add">+</button> </div>
</div>
<p class=" more">..more</p>
</div>
<div class="summaryButton">
<button class="button-left"><span>
<FontAwesomeIcon class="buttonLeft" icon="angle-left" /></span>Back</button>
<button class="button-right">Proceed to
payment
<FontAwesomeIcon class="buttonRight" icon="angle-right" /></button>
I want all information or data entered by user in booking page to be displayed on summary include each number on the plus and minus button too.
If I understand correctly you are having trouble using the LocalStorage API
The MDN page contains detailed examples on how to use localStorage or sessionStorage.
To set a value:
localStorage.setItem('nameOfItem', 'yourValue'); // The value must always be a string
And to get a value:
let value = localStorage.getItem('nameOfItem');
Note: since you explain that you are in dire need of a solution, for what I assume is an assignement from your studies, I'll add the next step of code. But do keep in mind, that the solution that you are asking is not related to your question. For future references read up on this page to get your answer faster and better. This will benefit you!
Addition: So what you are struggling with is to get your totalValue stored in the the localStorage whenever, I assume, a user want's to set a pickup date (based on your codepen). This translates to the problem:
How to store value in localStorage on submit of a form?
So for this to work, you need two things.
<form> element
submit event listener
So why do you need those? The form can be debatable on wether or not you need it. It will enable you to submit all the values in your form without selecting all form elements manually, but can add a bit of complexity if you might just need one value out of all the code. I'll use it for this example so you can utilize the submit event of a form.
This submit event is fired whenever a type="submit" input or button in a form has been clicked. This will be useful in cases where you need to process multiple values simultaneously and do, for example, a validation check on all of the values of the input elements.
So wrap a <form> element around your code and set an id attribute to it. This will make it easy to select it when we need it. Modify the button at the end of your form and add type="submit" to it. This will make the form submit whenever that button is clicked.
<form id="book">
<!-- Put all the form fields inside here -->
<button type="submit" name="send">Set pick up date</button>
</form>
So, now you've created your form let's go over to the JavaScript side of things.
First thing you'll want to do it is select the form. You gave it an unique id so use document.getElementById to select it.
// Select the form.
var bookForm = document.getElementById('book');
Since you now can submit your form listen for the submit event on the form to do something whenever your form has been submitted.
Inside your event function get your totalValue, the same way that you've done earlier in your code, and store it in a variable.
So now you've got your totalValue. From here you can add it to your localStorage. See the code below:
// Listen for submit event on form.
bookForm.addEventListener('submit', function(event) {
// Get the total value
var totalValue = totalValueElm.innerHTML;
// Store the total value in the localStorage.
localStorage.setItem('totalValue', totalValue);
// Now redirect to your second page.
// Prevent form from default submitting.
// This is important. Removing this line will make the form submit
// to the server and reload the page.
event.preventDefault();
});
You would now have stored your totalValue into the localStorage. And the value will be overwritten whenever you submit the form again.
So now on your next page you'll want to check if a value is stored in the localStorage. This part is explained at the top of my answer, but you'll need to add some checking to it.
var totalValue = localStorage.getItem('totalValue');
if (totalValue !== null) { // null is whenever no item of `totalValue` exists.
// Do something with your value.
}
So that's about it. This is as far as I can bring you. If you encounter any more problems try to open up a new thread on SO or ask a peer for help.
I hope you will be able to finish your project.
Best of luck!

Can't get val() from shopify input field

Shopify auto generates a form at checkout by way of {{ content_for_order_summary }} in which there is a gift card input:
<input placeholder=“Gift card or discount code” class=“field__input” data-
discount-field=“true” data-trekkie-id=“reduction_code_field”
autocomplete=“off” aria-required=“true” size=“30” type=“text”
name=“checkout[reduction_code]” id=“checkout_reduction_code”>
I am trying to get the value of this input with:
$(‘#checkout_reduction_code’).val();
This doesn't work though. I can inject a value then retrieve it just fine using the same selector so I know it's properly selected.
My question is exactly this:
Does anyone know why / had experience with this before? How on earth can i get the value of the input? My co-worker and i have tried almost everything.
Here's the working codepen with the whole form.
codepen
I am going to go with a guess here, well really just a pair of guesses.
First, attach the click event handler to the parent of the element. Works for dynamically created elements inside the parent wrapper better.
More importantly, prevent the submit which is the default type for a button however, you have also specifically set it to type="submit" - so it probably does that and gets in the way of your click handler.
Does it have value? force some in there
// just for the test, force content in there
$("#checkout_reduction_code").val("test me");
$('.order-summary__section').on("click", ".field__input-btn", function(event) {
event.preventDefault(); //prevent the default submit
console.log("Clicked");
console.log($("#checkout_reduction_code").length);
const cardCode = $("#checkout_reduction_code").val();
console.log(cardCode.length, ":" + cardCode + ":");
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="order-summary__section order-summary__section--discount" data-reduction-form="update">
<h3 class="visually-hidden">Gift card or discount code</h3>
<form class="edit_checkout animate-floating-labels" action="/8572698682/checkouts/ec0c9981ed8878d2c547acd14bc66825" accept-charset="UTF-8" method="post"><input name="utf8" type="hidden" value="✓"><input type="hidden" name="_method" value="patch"><input type="hidden" name="authenticity_token" value="uy4G7N445kuNoDYYrwNwZqANThycax+sD1ZgsPNdJEgpu7VFCRlc8EZtdGMSFfxNukx/VjJZK/9rapjY7nM8mg==">
<input type="hidden" name="step" value="payment_method">
</form>
<form class="edit_checkout animate-floating-labels" action="/8572698682/checkouts/ec0c9981ed8878d2c547acd14bc66825" accept-charset="UTF-8" method="post" _lpchecked="1"><input name="utf8" type="hidden" value="✓"><input type="hidden" name="_method" value="patch"><input type="hidden" name="authenticity_token" value="CIbBD3nktheTkrboQ8CE6WBzA4KDgv5iJFC6wMP+x3iaE3KmrsUMrFhf9JP+1gjCejIyyC2wyjFAbEKo3tDfqg==">
<input type="hidden" name="step" value="payment_method">
<div class="fieldset">
<div class="field field--show-floating-label">
<div class="field__input-btn-wrapper">
<div class="field__input-wrapper"><label class="field__label field__label--visible" for="checkout_reduction_code">Gift card or discount code</label>
<input placeholder="Gift card or discount code" class="field__input" data-discount-field="true" data-trekkie-id="reduction_code_field" autocomplete="off" aria-required="true" size="30" type="text" name="checkout[reduction_code]" id="checkout_reduction_code">
</div>
<button name="button" type="submit" class="field__input-btn btn" data-trekkie-id="apply_discount_button" aria-busy="false">
<span class="btn__content visually-hidden-on-mobile" aria-hidden="true">
Apply
</span>
<span class="visually-hidden">
Apply Discount Code
</span>
<svg class="icon-svg icon-svg--size-16 btn__icon shown-on-mobile" aria-hidden="true" focusable="false"> <use xlink:href="#arrow"></use> </svg>
<svg class="icon-svg icon-svg--size-18 btn__spinner icon-svg--spinner-button" aria-hidden="true" focusable="false"> <use xlink:href="#spinner-button"></use> </svg>
</button> </div>
</div>
</div>
</form>
</div>

Handlebars form submit does not work

I have a form that within a handlebars put the submit does not work, I have to do? Can anyone help?
<script id="chat-window-template" type="text/x-handlebars-template">
<i class="fa fa-times"></i>
<a href="#">
<span class="pull-left">
<img src="{{ user_image }}" width="40">
</span>
<span class="contact-name">{{user}}</span>
</a>
<div class="panel-body" id="chat-bill">
<form id="messageForm">
<input id="nameInput" type="hidden" class="input-medium" value="Macbook" />
<input id="messageInput" type="text" class="form-control" placeholder="Digite uma mensagem..." />
<input type="submit" value="Send" />
</form>
</div>
</script>
$("#messageForm").submit( function() {alert();});
Another possibility for running JS at the time of submit is to return false from the form element onsubmit attribute:
template.hbs
<form id="myForm" onsubmit="myFunc(); return false;">
<input id="textInput" type="text"></input>
<input type="submit"></input>
</form>
...
<script src="js/handleForm.js" type="text/javascript"></script>
handleForm.js
function myFunc() {
console.log("yo");
}
This will log "yo" in the browser console. It works because returning false in the onsubmit attribute prevents the browser's default action (which is an html thing, not a node or hbs thing). So, the page will not reload, and no query string will be added to the hyperlink, but myFunc() will run.
If you want to work with the form data, just get it from the DOM however you like. One option is to put an id on the text input (as I've done in template.hbs) and snag it with jQuery like so:
handleForm.js (revised)
function myFunc() {
var formText = $("#textInput").val();
// Do something with formText
}
Perhaps handler is attached before Dom is compiled, try listening t body events, and filtering by selector, should listen to Dom nodes added in future too...
$(document.body).on("submit", "#messageForm", function() {alert();});

Use javascript to close all opened forms before opening a new one

I'm making an html page with images, where the user clicks their image, to log in to their invoice page. This is what I have so far:
<script type="text/javascript">
function showHideForm(formid)
{
var _form = document.getElementById(formid);
_form.style.display = (_form.style.display != "block") ? "block" : "none";
return false;
}
</script>
<p>
<a href="#" onclick="return showHideForm('hiddenForm1');">
<img src="" width="150" height= "150" />
</a>
</p>
<form id="hiddenForm1" style="display: none;" method="post" action="user1_login.php">
<input type="text" name="user" placeholder="Name" required /><br />
<input type="text" name="pass" placeholder="Password" required/><br />
<input type="submit" name="Submit" value="Submit" />
</form>
<p>
<a href="#" onclick="return showHideForm('hiddenForm2');">
<img src="" width="150" height="150" />
</a>
</p>
<form id="hiddenForm2" style="display: none;" method="post" action="user2_login.php">
<input type="text" name="user" placeholder="Name" required /><br />
<input type="text" name="pass" placeholder="Password" required/><br />
<input type="submit" name="Submit" value="Submit" />
</form>
It works nicely except that if you click on other images, you get several instances open at the same time.
Is it possible to tack a bit of code to the beginning of the javascript to close any open instances before it runs the code to open a new form?
The logic is simple, have a class for openedform. On click event remove that class from the existing opened forms and add the class to the currently clicked form. Here is how to do it with jquery.
function showHideForm(formid)
{
// var _form=$("#"+formid); (in jquery)
var _form =document.getElementById(formid);
//select all existing opened forms and remove the class
$('.openedForm').removeClass('openedForm');
//add the openedForm class to the currently clicked
$(_form).addClass('openedForm');
return false;
}
Add the following code into your css file.
.openedForm
{
display:block !important;
}
If you need to alter the css of elements using javascript try to use classes. This way you make your javascript code cleaner and readable and your styling logic stays separated from the main logic.
Avoid using inline styles as much as possible.
Working demo:
https://jsbin.com/nigivu/edit?html,css,js,output

Categories