Generating dynamic ID's in HTML elements with jQuery - javascript

I'm builtin an web resume-generator to learn how to develop for web. I've made a HTML form which the user can add more fields to add more information's about him. Example: he has more than one professional experience, but the form starts with a single prof-exp field to fill. So he clicks in a "add new exp" button and the JS creates a new field for it. I used the clone() method from jQuery to do this, but this gives me with the problems I've listed bellow. Also, here's the code I've made:
var index = 0;
$(document).ready(() => {
$("#add-exp").click(() => {
$("#professional").clone().attr("id", "professional" + index++).
appendTo("#professional-info").find("select, input, textarea").val("");
})
})
<!DOCTYPE html>
<html>
<body>
<form action="" method="GET" id="main">
<fieldset id="professional-info">
<legend><h2>professional experience</h2></legend>
<div id="professional">
<label for="level">Nível: <select name="level" id="level" >
<option value="empty">Selecione</option>
<option value="estagio">Estágio</option>
<option value="junior-trainee">Junior/Trainee</option>
<option value="aux-opera">Auxiliar/Operacional</option>
<option value="pleno">Pleno</option>
<option value="senior">Sênior</option>
<option value="sup-coord">Supervisão/Coordenação</option>
<option value="gerencia">Gerência</option>
</select></label>
<label for="position"> Cargo: <input type="text" name="carrer" id="carrer" ></label><br>
<label for="company"> Empresa: <input type="text" name="company" id="company" ></label><br>
<label for="begin"> Início: <input type="month" name="begin" id="begin" ></label>
<label for="break"> Término: <input type="month" name="break" id="break" ></label>
<label for="stl-work"><input type="checkbox" name="stl-work" id="stl-work" >Ainda trabalho aqui</label><br>
<label for="job-desc"> Descrição: <textarea name="job-desc" id="job-desc" placeholder="Conte um pouco sobre o que você fazia lá." cols="40" rows="1"></textarea></label>
<button type="button" id="remove-exp" >Remove this professional xp</button>
</div>
<button type="button" form="main" id="add-exp">Add other professional exp</button>
</fieldset>
</form>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
</body>
</html>
The problems are:
Only the divs have dynamic ID's, which causes me the following two other problems;
I don't know how to implement the remove button logic, since I cannot make difference between the 1st button and the other ones from other divs;
Since the labels use their correspondent input ID to make reference, when the user clicks it, they point to the first field inputs;
I hope you guys could understand my problem and help me with it. Also, sorry for my english - i'm learning too. Thank you all!

As suggested, Vue.js is cool, but jQuery has some forgotten powers too.
And, since you create elements dynamically, don't use IDs.
And submit to the backend your experiences as arrays []: i.e: name="carrer[]", name="company[]" etc. Than on the backend loop those data arrays to retrieve all the user experiences.
const new_exp = () => $('<div>', {
'class': 'professional-exp',
html: `
<label>Nível:
<select name="level[]">
<option value="empty">Selecione</option>
<option value="estagio">Estágio</option>
<!-- etc... -->
</select>
</label>
<label>Cargo: <input type="text" name="carrer[]"></label><br>
<label>Empresa: <input type="text" name="company[]"></label><br>
<label>Início: <input type="month" name="begin[]"></label>
<label>Término: <input type="month" name="break[]" ></label>
<label><input type="checkbox" name="stl-work[]"> Ainda trabalho aqui</label><br>
<label>Descrição: <textarea name="job-desc[]" placeholder="Conte um pouco sobre o que você fazia lá." cols="40" rows="1"></textarea></label><br>
`,
append: $('<button>', {
type: 'button',
text: 'Remove',
click() {
$(this).closest('.professional-exp').remove();
}
}),
appendTo: '#professional',
});
jQuery($ => { // DOM ready and $ alias in scope
new_exp(); // On init (Create first exp)
$("#new_exp").on('click', new_exp); // On click
});
.professional-exp {
padding: 10px;
margin-bottom: 10px;
background: #eee;
}
<form action="" method="POST" id="main">
<fieldset>
<legend>
<h2>Professional experience</h2>
</legend>
<div id="professional"></div>
<button type="button" id="new_exp">+ Add more</button>
</fieldset>
</form>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
Above we're defining the Remove's button action right within the template, but if you want you can also hardcode the button into the template and create a dynamic click handler (using jQuery's .on()) like:
const exp_new = () => $('<div>', {
'class': 'professional-exp',
html: `
<label>Nível:
<select name="level[]">
<option value="empty">Selecione</option>
<option value="estagio">Estágio</option>
<!-- etc... -->
</select>
</label>
<label>Cargo: <input type="text" name="carrer[]"></label><br>
<label>Empresa: <input type="text" name="company[]"></label><br>
<label>Início: <input type="month" name="begin[]"></label>
<label>Término: <input type="month" name="break[]" ></label>
<label><input type="checkbox" name="stl-work[]"> Ainda trabalho aqui</label><br>
<label>Descrição: <textarea name="job-desc[]" placeholder="Conte um pouco sobre o que você fazia lá." cols="40" rows="1"></textarea></label><br>
<button class="exp_delete">REMOVE</button>
`,
appendTo: '#professional',
});
jQuery($ => { // DOM ready and $ alias in scope
exp_new(); // On init (Create first exp)
$("#exp_new").on('click', exp_new); // and on click.
$('#main').on('click', '.exp_delete', ev => $(ev.target).closest('.professional-exp').remove());
});
.professional-exp {
padding: 10px;
margin-bottom: 10px;
background: #eee;
}
<form action="" method="POST" id="main">
<fieldset>
<legend>
<h2>Professional experience</h2>
</legend>
<div id="professional"></div>
<button type="button" id="exp_new">+ Add more</button>
</fieldset>
</form>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>

Details of demo code are commented in the code itself. There are minor changes to some classes for <fieldset>s and <button>s. The structure is altered a little so keep that in mind. jQuery is versatile and it allows you to generalize DOM operations and do away with dependency on ids -- it's very possible to just use classes.
Events registered to dynamic tags fail unless you delegate events. To delegate a click event to all buttons existing currently and in the future, register an ancestor tag that the buttons commonly share (ex. #main). Then assign the selectors of the buttons in the second parameter (event data):
$('#main').on('click', '.add, .remove', function(e) {...
As for removing a by clicking a nested button -- $(e.target) and $(this) can be used to reference the button that was currently clicked. When you need to find the appropriate ancestor of a clicked button (ex. .professional) use .closest() method like so:
$(e.target).closest('.professional').remove();
Demo
let index = 0;
// Hide the first .remove button
$('#remove').hide();
/*
Register the form to the click event
Event data directs .add and .remove buttons
*/
$("#main").on('click', '.add, .remove', function(e) {
// if the clicked button has .add
if ($(this).hasClass('add')) {
/*
clone the first .professional
increment counter
Reference all form controls of the clone
on each form control modify its id
*/
const dupe = $(".professional:first").clone(true, true);
index++;
const formControls = dupe.find('select, button, input, textarea');
formControls.each(function() {
let ID = $(this).attr('id');
$(this).attr('id', ID + index);
});
/*
Remove the legend from clone
Show the .add and .remove on clone
Hide the clicked button
Add clone to form
Stop event bubbling
*/
dupe.find('legend').remove();
dupe.find('.add, .remove').show();
$(e.target).hide();
$('#main').append(dupe);
e.stopPropagation();
// Otherwise if clicked button has .remove...
} else if ($(e.target).hasClass('remove')) {
/*
Find clicked button ancestor .professional and remove
it.
Hide all .add buttons
Show the last .add
Stop event bubbling
*/
$(e.target).closest('.professional').remove();
$('.add').hide();
$('.add:last').show();
e.stopPropagation();
} else {
// Otherwise just stop event bubbling
e.stopPropagation();
}
});
:root {
font: 400 14px/1 Consolas
}
fieldset {
width: fit-content
}
legend {
margin-bottom: -15px
}
label {
display: block
}
input,
select,
button {
display: inline-block;
font: inherit;
height: 3ex;
line-height: 3ex;
vertical-align: middle
}
.text input {
width: 24ch
}
select {
line-height: 4ex;
height: 4ex;
}
label b {
display: inline-block;
width: 7.5ch;
}
button {
position: absolute;
display: inline-block;
height: initial;
margin: 0;
}
.add {
position: absolute;
right: 0;
}
[for=level] b {
width: 6ch
}
.btn-grp {
position: relative;
width: 97%;
min-height: 26px;
padding: 0
}
<!DOCTYPE html>
<html>
<head></head>
<body>
<form action="" method="GET" id="main">
<fieldset class="professional">
<legend>
<h2>Professional Experience</h2>
</legend>
<label for="level">
<b>Nível: </b>
<select name="level" id="level">
<option value="empty">Selecione</option>
<option value="estagio">Estágio</option>
<option value="junior-trainee">
Junior/Trainee
</option>
<option value="aux-opera">
Auxiliar/Operacional
</option>
<option value="pleno">Pleno</option>
<option value="senior">Sênior</option>
<option value="sup-coord">
Supervisão/Coordenação
</option>
<option value="gerencia">
Gerência
</option>
</select>
</label>
<fieldset class='text'>
<label for="carrier"><b>Cargo: </b>
<input type="text" name="carrer" id="carrer">
</label>
<label for="company"><b>Empresa: </b>
<input type="text" name="company" id="company">
</label>
<label for="begin"><b>Início: </b>
<input type="month" name="begin" id="begin">
</label>
<label for="break"><b>Término: </b>
<input type="month" name="break" id="break">
</label>
</fieldset>
<label for="stl-work">
<input type="checkbox" name="stl-work" id="stl-work" >Ainda trabalho aqui
</label>
<label for="job-desc"><b>Descrição: </b></label>
<textarea name="job-desc" id="job-desc" placeholder="Conte um pouco sobre o que você fazia lá." cols="35" rows="1"></textarea>
<fieldset class='btn-grp'>
<button type="button" id='remove' class='remove'>Remove</button>
<button type="button" id='add' class="add">Add</button>
</fieldset>
</fieldset>
</form>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
</body>
</html>

if you already wrap your input inside a label, you dont need id anymore,
and you can use this as a parameter of delete button, so you can use it to delete your block.
Please check the following example
$(function(){
// keep the first block hidden as an empty template
$('.form-row:first').hide();
// trigger add new item
AddItem();
})
function AddItem(){
var container = $('#container');
// clone the form, show it & append before add button
var cloned = $('.form-row:first').clone().show().insertBefore($('#addBtn'));
}
function RemoveItem(elm){
// get form element & remove it
$(elm).closest('.form-row').remove()
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
<script src="https://code.jquery.com/ui/1.12.1/jquery-ui.js"></script>
<style type="text/css">
.form-row {border:1px solid #ccc; margin:5px 0;padding:10px;}
</style>
<div id="container">
<div class="form-row">
<!-- wrap your input inside label tag to avoid using id as reference -->
<label>Field 1 : <input type="text" name="field1"></label>
<label>Field 2 : <input type="text" name="field2"></label>
<input type="button" value="Remove this item" onclick="RemoveItem(this)">
</div>
<input id="addBtn" type="button" value="Add new item" onclick="AddItem()">
</div>

One way to connect your new "remove" button with its "professional" div would be to add an extra statement in your event handler to update its id parallel to the new div's id, something like:
let lastIndex = index - 1;
$("#professional" + lastIndex).find("button").attr("id", "add-exp" + lastIndex);
(This code may not have the correct syntax -- I don't use jQuery very much -- but you can see the idea.)
A better way might be, when the "remove" button is clicked, don't remove according to ID, but instead find the closest ancestor div and remove that div.
For labels, you should leave out the ids (because no two elements on the same page should ever have the same id). And because the inputs are nested in the labels, you should be able to leave out the for attribute as well and let the the association be implicit. (See https://developer.mozilla.org/en-US/docs/Web/HTML/Element/label.)

Related

How to fix my Javascript for checkbox questions/answers with feedback?

I need help figuring out how to get each checkbox give back a different modal. I figured if it was fine with showing two modals that I could add another one in but it did not work. I want it to give separate feedback for each answer chosen. So for example if Answer A is chosen, then I want Modal1 for feedback. Same for B but Modal 2. Choice C works as being correct already.
<form id="submit1">
<div id="question1" class="p-body flex-col-left" style="justify-content: flex-start;">
<label>
<input type="checkbox" name="input" value="wrongA" class="question1" data-seen=1>
<span class="lineup">A. Emily should cross through the incorrect information and write in
the correct information.</span>
</label>
<br>
<label>
<input type="checkbox" name="input" value="wrongB" class="question1" data-seen=2>
<span class="lineup">B. Emily does not need to do anything until it is time for her to renew
her license.</span>
</label>
<br>
<label>
<input type="checkbox" name="input" value="right" class="question1" data-seen=0>
<span class="lineup">C. Emily needs to fill out the appropriate paperwork and submit it to
the TDLR. </span>
</label>
<br>
<br>
</div>
<div class="flex-center">
<button type="submit" id="answer" class="modal-button modal-a">Submit</button>
</div>
</form>
<script>
document.getElementById("answer").onclick = validate;
function validate() {
var checkboxes = document.getElementsByName("input");
var checkboxChecked = [];
for (var i = 0; i < checkboxes.length; i++) {
if (checkboxes[i].checked && (checkboxes[i].value === "right")) {
checkboxChecked.push(checkboxes[i]);
}
}
for (var i = 1; i < checkboxes.length; i++) {
if (checkboxes[i].checked && (checkboxes[i].value === "wrongA")) {
checkboxWrongA.push(checkboxes[i]);
}
}
for (var i = 2; i < checkboxes.length; i++) {
if (checkboxes[i].checked && (checkboxes[i].value === "wrongB")) {
checkboxWrongB.push(checkboxes[i]);
}
}
if (checkboxChecked.length === 1) {
const modal1 = document.querySelector('#modal1');
modal1.style.display = 'block'
window.nextBtn.style.display = "block";
window.dropdown.style.display = "block";
window.breadcrumb.style.display = "block";
}
if (checkboxWrongA.length === 1) {
const modal2 = document.querySelector('#modal2');
modal2.style.display = 'block'
}
if (checkboxWrongB.length === 1) {
const modal3 = document.querySelector('#modal3');
modal3.style.display = 'block'
}
}
</script>
Because you mentioned that your users should only choose 1 option, then a set radio buttons would be the way to go. Then we need to evaluate your JS to work with the radio buttons.
First of make sure that each radio has the same name attribute value. This enables us to use the name to get the value from the input that is checked. Allow the value to represent the chosen option. You could use right or wrongA here, but that doesn't say much about which option is chosen, only it's correctness.
<input type="radio" name="answer" value="A">
<input type="radio" name="answer" value="B">
<input type="radio" name="answer" value="C">
Now on the JavaScript side, we can make things simpler. Since you're using a <form> element, I'd suggest that you listen for a submit event on the form to know when you clicked your validate button.
A submit event happens whenever you have <form> with a <button type="submit"> inside of it and click the button. By default the page would reload. We'll need to stop that from happening by preventing the default behavior of the form. (see event.preventDefault()).
We can extract the data from the form using a FormData object. This object does a lot of work for us, for example, figuring out which of our inputs have been checked. With that object, we can ask for the value of the input by using the name of the input. In this case, I used the name: 'answer'.
Because we now use radio inputs, the value can only be either A, B or C. Based on the answer, look for the appropriate modal and show it.
const form = document.querySelector('#question-form');
form.addEventListener('submit', event => {
event.preventDefault();
const formData = new FormData(form);
const answer = formData.get('answer');
if (answer === 'A') {
const modal = document.querySelector('#modal1');
modal.classList.add('show');
} else if (answer === 'B') {
const modal = document.querySelector('#modal2');
modal.classList.add('show');
} else if (answer === 'C') {
const modal = document.querySelector('#modal3');
modal.classList.add('show');
}
});
.modal {
display: none;
position: fixed;
top: 50%;
left: 50%;
translate: -50% -50%;
background: pink;
padding: 50px 100px;
}
.modal.show {
display: block;
}
<form id="question-form">
<div>
<label>
<input type="radio" name="answer" value="A" class="question1" data-seen=1>
<span class="lineup">A. Emily should cross through the incorrect information and write in the correct information.</span>
</label>
</div>
<div>
<label>
<input type="radio" name="answer" value="B" class="question1" data-seen=2>
<span class="lineup">B. Emily does not need to do anything until it is time for her to renew her license.</span>
</label>
</div>
<div>
<label>
<input type="radio" name="answer" value="C" class="question1" data-seen=0>
<span class="lineup">C. Emily needs to fill out the appropriate paperwork and submit it to the TDLR.</span>
</label>
</div>
<button type="submit" class="modal-button modal-a">Submit</button>
</form>
<div id="modal1" class="modal">
Modal 1
</div>
<div id="modal2" class="modal">
Modal 2
</div>
<div id="modal3" class="modal">
Modal 3
</div>
In this example checkboxes are replaced by radio buttons since it looks as if there's only one correct answer (and multiple modals are silly). The modals are <dialog> elements. Also, the "answer" button triggers a "submit" event that's registered to the <form>. The event handler check(e) is designed to leverage control by event delegation. In addition, the HTMLFormElement and HTMLFormControlsCollection interfaces were used to reference the <form> and it's form controls (all <input> in the <form>).
Details are commented in example
/**
* Register the <form> to the "submit" event
* The event handler check(e) is called when a "submit" event is triggered
*/
document.forms.quiz.onsubmit = check;
/**
* The event handler passes the Event object by default
* Prevent the default behavior of <form> during "submit" event
* Collect all HTMLFormControls into a HTMLCollection
* If a one form control with [name="chx"] is .checked...
* ...get the checked radio button's value...
* ...reference the <dialog> with the class of the same value as the
* checked radio button...
* ...then open that <dialog>
*/
function check(e) {
e.preventDefault();
const fc = this.elements;
if (Array.from(fc.rad).some(c => c.checked)) {
const answer = fc.rad.value;
const modal = document.querySelector('.' + answer);
modal.showModal();
}
}
/**
* Collect all .close (buttons) into a NodeList and bind each one to the
* "click" event.
* The event handler close(e) is called when the "click" event is triggered.
*/
document.querySelectorAll(".close").forEach(btn => btn.onclick = close);
/**
* This event handler will close the parent <dialog> of the clicked <button>
*/
function close(e) {
this.closest('dialog').close();
}
html {
font: 300 2ch/1.2 'Segoe UI'
}
body {
min-height: 100vh;
}
body,
form {
display: flex;
flex-flow: column nowrap;
justify-content: center;
align-items: center;
margin: 0 20px;
}
form {
padding: 10px 30px;
}
fieldset {
width: min(50vw, 500px);
}
legend {
font-size: 1.25rem;
}
menu {
margin: 0 20px 10px 0;
}
.list {
list-style-type: lower-latin;
margin: 20px 30px 0 -10px;
}
label {
display: inline-flex;
align-items: flex-start;
transform: translate(12px, -17px);
line-height: 1;
}
button,
input {
font: inherit;
cursor: pointer;
}
.right * {
float: right;
margin-bottom: 20px;
}
<form id="quiz">
<fieldset>
<legend>Quiz</legend>
<menu class="list">
<li><label>
<input name="rad" type="radio" value="A"> Emily should cross through the incorrect information and write in the correct information.
</label></li>
<li><label>
<input name="rad" type="radio" value="B"> Emily does not need to do anything until it is time for her to renew her license.
</label></li>
<li><label>
<input name="rad" type="radio" value="C"> Emily needs to fill out the appropriate paperwork and submit it to the TDLR.
</label></li>
</menu>
<menu class="right">
<button>Done</button>
</menu>
</fieldset>
</form>
<dialog class="A">
<fieldset>
<legend>Message A</legend>
<p>Quote mode. It's called carpe diem Morty. Look it up. Morty! The principal and I have discussed it, a-a-and we're both insecure enough to agree to a three-way! Haha god-damn! </p>
<menu class="right">
<input class="close" type="button" value="OK">
</menu>
</fieldset>
</dialog>
<dialog class="B">
<fieldset>
<legend>Message B</legend>
<p>Yo! What up my glip glops! Lookin' good! Are you kidding? I'm hoping I can get to both of them, Rick! I mixed in some praying mantis DNA. You know a praying mantis is the exact opposite of a vole, Morty? They mate once and then bluergh cut each other's
heads off. </p>
<menu class="right">
<input class="close" type="button" value="OK">
</menu>
</fieldset>
</dialog>
<dialog class="C">
<fieldset>
<legend>Message C</legend>
<p>Get off the high road Summer. We all got pink eye because you wouldn't stop texting on the toilet. Flip the pickle over. Honey, stop raising your father's colesterol so you can take a hot funeral selfie. God? God's turning people into insect monsters
Beth. I'm the one beating them to death. Thank me. </p>
<menu class="right">
<input class="close" type="button" value="OK">
</menu>
</fieldset>
</dialog>

Hide virtual keyboard after selecting value on Select2 v3

I'm using select2 v.3.4.5 solely for my project. Currently, when viewing on mobile, the keyboard does not close after it open and value selected, I would like to close it thought. I would like open only when user focus on it and type something.
$(document).ready(function() {
$('#Salutation,#Gender').select2()
.on('change select-open', function(e) {
setTimeout(function() {
$('.select2-input').blur();
}, 500);
});
})
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<link href="https://stackpath.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet">
<script src="https://stackpath.bootstrapcdn.com/bootstrap/3.3.5/js/bootstrap.min.js"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/select2/3.4.5/select2.css" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/select2/3.4.5/select2.min.js"></script>
<div class="container">
<div class="row">
<div class="col-xs-4 col-xs-offset-4">
<h3>Application Form</h3>
<form class="form" action="/action_page.php">
<div class="form-group">
<label for="GivenName">Given Name:</label>
<input class="form-control" type="text" id="GivenName">
</div>
<div class="form-group">
<label for="Surname">Surname:</label>
<input class="form-control" type="text" id="Surname">
</div>
<div class="form-group">
<label for="Salutation">Salutation:</label>
<select class="" name="" id="Salutation">
<option value="Mrs">Mrs</option>
<option value="Mr">Mr</option>
<option value="Miss">Miss</option>
</select>
</div>
<div class="form-group">
<label for="Gender">Gender:</label>
<select class="" name="" id="Gender">
<option value="Female">Female</option>
<option value="Male">Male</option>
<option value="Transgender">Transgender</option>
</select>
</div>
<button type="submit" class="btn btn-default">Submit</button>
</form>
</div>
</div>
</div>
As this js
$('#Salutation,#Gender').select2()
.on('change select2-open',function(e){
setTimeout(function(){
$('.select2-input').blur();
}, 500);
});
I set input-search box to blur already but keyboard does not close.
How can I do to archive this purpose? Please kindly help. Thanks.
PS: Understandably, select2 v4 fixed this bug, yet I could not upgrade my select2 version since my project is solely depend on v3.*
Ensuring the search box does not autofocus
There is no way to do this well in Select2 - whenever you try to call the blur() function on this input, it just refocuses it.
However, by listening on the open event, we can replace the search box with our own one, that does not autofocus. Only the currently active search box has the class select2-focused, so we use that to find it, and then create a new search box (with the same select2-input class so it retains the same look and feel), and then re-implement the search feature ourselves, finally inserting that into the DOM, and removing the old search box.
Not showing the keyboard after closing the selection popup
Select2 seems to try and implement its own blur() event in a very weird way (see here).
So, rather than try and use that, take advantage of CSS selectors. The :focus selector in CSS selects anything that has focus. Since Select2 doesn't actually add any new DOM elements (i.e. once in the HTML, it becomes standard <div> elements, <input> elements, etc), we can find the one that has focus, and successfully call blur on it.
Therefore, by calling $(":focus").blur(), we find the DOM element that currently has focus, and we blur it.
Also, by using select2-close as our event, rather than change, the keyboard won't open even if the user doesn't select an item, but instead clicks outside of it.
I have tested it, and it does work for me on an iPad running iOS 11. Here is the final, working code:
$(document).ready(function() {
$("#Salutation,#Gender").select2().on("select2-open",()=>{
let oldSearchBox = $(".select2-focused")[0]; //Get the current search box
let parent = oldSearchBox.parentNode; //The parent of the search box (i.e. the element that holds it)
let search = document.createElement("input"); //Create a new input box
search.classList.add("select2-input"); //Make it look like the old one
search.addEventListener("keyup", ()=>{ //Whenever someone releases a key, filter the results
let results = parent.parentNode.getElementsByClassName("select2-result"); //Get all of the select box options
let query = search.value.toLowerCase(); //Get what the user has typed (in lower case so search is case-insensitive)
for (let result of results) { //Loop through all of the select box options
let resultText = result.children[0].childNodes[1].nodeValue.toLowerCase(); //Get the text for that option (also in lower case)
result.style.display = (resultText.indexOf(query)==-1) ? "none" : "block"; //If the result text contains the search, it is displayed, otherwise it is hidden
}
})
parent.appendChild(search); //Add the new search box to the page
oldSearchBox.remove(); //Remove the old one
});
$("#Salutation,#Gender").select2().on("select2-close",()=>{
setTimeout(()=>{
$(":focus").blur();
}, 50);
});
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<link href="https://stackpath.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet">
<script src="https://stackpath.bootstrapcdn.com/bootstrap/3.3.5/js/bootstrap.min.js"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/select2/3.4.5/select2.css" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/select2/3.4.5/select2.min.js"></script>
<div class="container">
<div class="row">
<div class="col-xs-4 col-xs-offset-4">
<h3>Application Form</h3>
<form class="form" action="/action_page.php">
<div class="form-group">
<label for="GivenName">Given Name:</label>
<input class="form-control" type="text" id="GivenName">
</div>
<div class="form-group">
<label for="Surname">Surname:</label>
<input class="form-control" type="text" id="Surname">
</div>
<div class="form-group">
<label for="Salutation">Salutation:</label>
<select class="" name="" id="Salutation">
<option value="Mrs">Mrs</option>
<option value="Mr">Mr</option>
<option value="Miss">Miss</option>
</select>
</div>
<div class="form-group">
<label for="Gender">Gender:</label>
<select class="" name="" id="Gender">
<option value="Female">Female</option>
<option value="Male">Male</option>
<option value="Transgender">Transgender</option>
</select>
</div>
<button type="submit" class="btn btn-default">Submit</button>
</form>
</div>
</div>
</div>
I have an alternative work around that I've been using for about a week now. So far it seems to work well on all android and ios devices I've tried. I use this on select2 instances that have 'multiple' set to false (i.e. 'single' type). In this case, I want the user to make a single selection, and the the keyboard should disappear.
In short, during the select2-close event you set a flag that indicates that you want to disable any focus event the select2-focusser receives. When the focus event is triggered, you check if the flag is set and if so you simply move the focus to another target. I add the flag as a data property and I reset this flag after a second using a setTimeOut.
This works because the select2 close handler is split into 2 parts, and the 'select2-close' event is triggered at the end of part 1 (and before part 2). Part 1 is the 'abstract' close handler, which actually closes the selection dialog, and sets the control's value. Part 2 is the 'single' close handler, which really just causes the select2 to refocus on itself (which is the problem!).
For me, I added a '.focus-bait' class to one of my nav bar buttons, and I use this to divert focus during the focusser's focus event execution. If you have issues getting this refocus step to work, try a different element (I had a problem getting it to work on a button I had made for the purpose of focusing on. I'm still not sure why, but I didn't investigate more as my nav button solution worked perfectly for my needs).
$('body').on('focus','.select2-focusser', function(e) {
let isDisabled = $(this).parent().first().data("disable-focus");
if (isDisabled) {
console.log('Focusser: focus event aborted');
$('.focus-bait').focus();
}
});
//select2_focus_ctrl is a class that I add to any select2 container
//that I wish to use this focus logic e.g. add it to #Salutation,#Gender
$('body').on('select2-close','select2_focus_ctrl', function(e) {
console.log('Focusser: disabling focus event');
if ($(this).data('select2').opts.multiple != true) {
$(this).prev().data("disable-focus",true);
setTimeout(function() {
console.log('Focusser: enabling focusser');
$(this).prev().data("disable-focus",false);
}, 1000);
}
});
Here is a full code snippet. While writing it I noticed that if the select2 container is sourced from a 'select' element, the 'multiple' property does not exist (mine used a 'div'), so I've changed one line of code: .opts.multiple != true (instead of == false).
$(document).ready(function() {
$('#Salutation').select2(
);
$('body').on('focus','.select2-focusser', function(e) {
let isDisabled = $(this).parent().first().data("disable-focus");
if (isDisabled) {
console.log('Focusser: focus event aborted');
$('.focus-bait').focus();
}
});
$('body').on('select2-close','.select2_focus_ctrl', function(e) {
console.log('Focusser: disabling focus event');
if ($(this).data('select2').opts.multiple != true) {
$(this).prev().data("disable-focus",true);
setTimeout(function() {
console.log('Focusser: enabling focusser');
$(this).prev().data("disable-focus",false);
}, 1000);
}
});
$('body').on('change','#Salutation', function(e) {
let theVal = $('#Salutation').select2("val");
$('#currentVal').val(theVal);
});
});
body {
background: #dddddd;
padding: 20px;
font-family: Helvetica;
}
button {
background: #0084ff;
border: none;
border-radius: 5px;
padding: 8px 14px;
font-size: 15px;
color: #fff;
cursor: pointer;
}
button:focus {
background: #fff;
color: red;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<link href="https://cdnjs.cloudflare.com/ajax/libs/select2/3.5.4/select2.css" rel="stylesheet"/>
<script src="https://cdnjs.cloudflare.com/ajax/libs/select2/3.5.4/select2.js"></script>
<button type="button" class="focus-bait">
Just the bait!
</button>
<br>
<div>
<input id="currentVal" style="height:20px;width:150px"/>
</div>
<br><br>
<select class="select2_focus_ctrl" id="Salutation" style="width:200px;">
<option value="Mrs">Mrs</option>
<option value="Mr">Mr</option>
<option value="Miss">Miss</option>
<option value="Esquire">Esquire</option>
<option value="Other Options">Other Options</option>
<option value="Just for interest">Interesting longer item</option>
</select>

How to align checkboxes horizontally next to each other and show form upon click?

i have been searching stackoverflow but, I am unable to find a solution pertaining to my problem. What i want is when a user clicks a checkbox, a form shows up. I managed to get one checkbox and make the form show up, but having trouble putting checkboxes side by side next to each other. So what i want is:
[] residential [] commercial [] industrial.
When a user clicks the above boxes, a form opens up on the same page. My code:
edit: Thanks to #brk for the solution. After copying the solution below, for some reason When i click on 'residential', i don't see anything popping up. My code:
<script>
// Add event listener to each of the radio button
document.querySelectorAll('input[name="selectType"]').forEach(function(item) {
item.addEventListener('click', function(e) {
// get the data-type from this, this data-type will be used to
// hide show relevant form
let name = this.getAttribute('data-type');
// hide previously displayed form
if (document.querySelector('.formDisplay') !== null) {
document.querySelector('.formDisplay').classList.remove('formDisplay');
}
// find the form whose name+_info is same as the name of the form
// & add a formDisplay to it
document.querySelector('form[name="' + name + '_info"]').classList.add('formDisplay');
})
})
</script>
<style>
form {
display: none;
}
.formDisplay {
display: block;
}
</style>
<html>
<body>
<div class="form-group">
<input id="checkboxR" data-type='res' name='selectType' type='radio'>
<label for="checkboxR">Residential</label>
<input id="checkboxC" data-type='com' name='selectType' type='radio'>
<label for="checkboxC">Commercial</label>
<input id="checkboxI" data-type='ind' name='selectType' type='radio'>
<label for="checkboxI">Industrail</label>
</div>
<form name="res_info" action="/result/" method="get" class="form-horizontal" role="form">
Residential Form
</form>
<form name="com_info" action="/result/" method="get" class="form-horizontal" role="form">
Commercial Form
</form>
<form name="ind_info" action="/result/" method="get" class="form-horizontal" role="form">
Industrial Form
</form>
</body>
</html>
So, I see 3 radio buttons, but clicking on them doesn't open up anything. Sorry im kinda new to html/css so this concept is new.
Make changes in your html and add another attribute data-type .This value will be used to display corresponding form.
Create tep css and initially hide all the forms. Add another class to display the form using display:block
Onclick of the radio button get the data-type find the forma whose name matches with this data-type and display that form
// Add event listener to each of the radio button
document.querySelectorAll('input[name="selectType"]').forEach(function(item) {
item.addEventListener('click', function(e) {
// get the data-type from this, this data-type will be used to
// hide show relevant form
let name = this.getAttribute('data-type');
// hide previously displayed form
if (document.querySelector('.formDisplay') !== null) {
document.querySelector('.formDisplay').classList.remove('formDisplay');
}
// find the form whose name+_info is same as the name of the form
// & add a formDisplay to it
document.querySelector('form[name="' + name + '_info"]').classList.add('formDisplay');
})
})
form {
display: none;
}
.formDisplay {
display: block;
}
<body>
<div class="form-group">
<input id="checkboxR" data-type='res' name='selectType' type='radio'>
<label for="checkboxR">Residential</label>
<input id="checkboxC" data-type='com' name='selectType' type='radio'>
<label for="checkboxC">Commercial</label>
<input id="checkboxI" data-type='ind' name='selectType' type='radio'>
<label for="checkboxI">Industrail</label>
</div>
<form name="res_info" action="/result/" method="get" class="form-horizontal" role="form">
Residential Form
</form>
<form name="com_info" action="/result/" method="get" class="form-horizontal" role="form">
Commercial Form
</form>
<form name="ind_info" action="/result/" method="get" class="form-horizontal" role="form">
Industrial Form
</form>
</body>
This worked for me
<body>
<div class="form-group">
<div class="horizontal">
<input id="checkbox" type=checkbox> Residential</input>
</div>
<div class="horizontal" id="delivery_div" style="display:none">
<input id="delivery" type=checkbox > etc </input>
</div>
</div>
<script>
var checkbox = document.getElementById('checkbox');
var delivery_div = document.getElementById('delivery_div');
checkbox.onclick = function() {
console.log(this);
if(this.checked) {
delivery_div.style['display'] = 'block';
} else {
delivery_div.style['display'] = 'none';
}
};
</script>
<style>
<body>
<div class="form-group">
<div class="horizontal">
<input id="checkbox" type=checkbox> Residential</input>
</div>
<div class="horizontal" id="delivery_div" style="display:none">
<input id="delivery" type=checkbox > etc </input>
</div>
</div>
<script>
var checkbox = document.getElementById('checkbox');
var delivery_div = document.getElementById('delivery_div');
checkbox.onclick = function() {
console.log(this);
if(this.checked) {
delivery_div.style['display'] = 'block';
} else {
delivery_div.style['display'] = 'none';
}
};
</script>
<style>
.horizontal {
display: inline-block;
float:left;
}
</style>
</body>
.horizontal {
display: inline-block;
float:left;
}
</style>
</body>

Show a dialog box next to a textarea without pop-up or alert box

I want to show a short message besides a textarea once we click inside the textarea.
Here is the simple code that i need to modify :
<textarea id="txt" ></textarea>
and :
$('#txt').click(function(){
// what should i put here to show a dialogbox besides textarea ?
});
Here is a fiddle demo except that i need to put whatever i want as a message once we click inside textarea.
I am a complete newbie so please bear with me if i didn't put things the way it should. Thank you.
input, textarea, select {
display: block;
}
<form>
<p>Try submitting an empty form and one with invalid email addresses.</p>
<label for="one">One Email: </label><input type="email" id="one" required data-errormessage-value-missing="Something's missing" data-errormessage-type-mismatch="Invalid!">
<label for="another">Another Email: </label><input type="email" id="another" data-errormessage="Generic error message">
<label for="textarea">A textarea: </label><textarea id="textarea" required data-errormessage-value-missing="Add some text"></textarea>
<select required data-errormessage-value-missing="Please, pick one">
<option selected disabled value="">Pick one</option>
<option value="1">One</option>
</select>
<button>Submit</button>
</form>
You could do something like the following - and I would use focus rather than click . The following code adds the text message you need besides your textarea.
If you want to style the message with an arrow too, have a look at this: cssarrowplease.com
// show hidden message on focus
$('#txt').on('focus', function() {
$('#txt-message').show();
});
// hide hidden message on blur - optional extra
$('#txt').on('blur', function() {
$('#txt-message').hide();
});
/* start message hidden */
#txt-message {
display:none;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<textarea id="txt"></textarea>
<span id="txt-message">message here</span>
Add:
<span id='shortMessage'></span>
tag right after the textarea div.
Then replace your comment with:
$('#shortMessage').innerHTML('your message');
Hope this will help you .
$(document).ready(function() {
$('#textarea').click(function() {
$('#showMsgId').text("some message .......")
});
});
input,
textarea,
select {
display: block;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<form>
<p>Try submitting an empty form and one with invalid email addresses.</p>
<label for="one">One Email:</label>
<input type="email" id="one" required data-errormessage-value-missing="Something's missing" data-errormessage-type-mismatch="Invalid!">
<label for="another">Another Email:</label>
<input type="email" id="another" data-errormessage="Generic error message">
<label for="textarea">A textarea:</label>
<div style="width: 100%; overflow: hidden;">
<textarea id="textarea" required data-errormessage-value-missing="Add some text" style="width: 200px; float: left;"></textarea>
<span id="showMsgId" style="margin-left: 10px;"></span>
</div>
<select required data-errormessage-value-missing="Please, pick one">
<option selected disabled value="">Pick one</option>
<option value="1">One</option>
</select>
<button>Submit</button>
</form>
Here is the fiddle for my non-JavaScript alternative.
Pete's answer works if you are satisfied with simply displaying text. If you need the info-box to look pretty and be displayed above the other elements then you will need something different like this:
CSS:
.cPopTable {
position: absolute;
top: 50px;
right: 200px;
background-color: #ffffff;
z-index: 10;
display:none;
}
.cContainer:active .cPopTable {
display:table;
}
HTML:
<div class="cContainer">
<textarea id="txt">Default text here</textarea>
<table class="cPopTable"><tr><td>message pop-up here</td></tr></table>
</div>
Also, the benefit is not using any JavaScript. In fact, unless you are worried about really old browsers you can easily handle all the tasks like these using CSS z-index and event-processing as shown in this example.
You can easily change the position and the way the box is displayed (font, background, etc.) by working with the cPopTable CSS.

Dynamically adding and removing div using JQuery?

I'm trying to create a form that allows a user to enter their experience and education
I would like the user to be able to add and remove education or experience.
I am able to do this... sort of. Only the problem is my new divs that I am creating are being appended to the end of the page instead of being appended after the previous div.
These are my scripts:
$(document).ready(function() {
var inputs = 1;
$('#btnAdd').click(function() {
$('.btnDel:disabled').removeAttr('disabled');
var c = $('.clonedInput:first').clone(true);
c.children(':text').attr('name', 'input' + (++inputs));
$('.clonedInput:last').after(c);
});
$('.btnDel').click(function() {
if (confirm('continue delete?')) {
--inputs;
$(this).closest('.clonedInput').remove();
$('.btnDel').attr('disabled', ($('.clonedInput').length < 2));
}
});
});
$(document).ready(function() {
var inputs = 1;
$('#btnAdd2').click(function() {
$('.btnDel2:disabled').removeAttr('disabled');
var c = $('.clonedInput:first').clone(true);
c.children(':text').attr('name', 'input' + (++inputs));
$('.clonedInput:last').after(c);
});
$('.btnDel2').click(function() {
--inputs;
$(this).closest('.clonedInput').remove();
$('.btnDel2').attr('disabled', ($('.clonedInput').length < 2));
});
});
I understand it's bad form to duplicate code like this but I'm not sure how else to else to do it so that clicking the add button doesn't get pressed for the wrong div...
and my html is:
<form id="myForm">
<h2>Education</h2>
<p>Please add all of your education</p>
<div style="margin-bottom: 4px; border: 2px solid; border-style: dashed" class="clonedInput">
Level: <select>
<option value="secondary">Secondary</option>
<option value="someps">Some Post Secondary</option>
<option value="college">College</option>
</select> <br /> <br />
Did you receive a degree, diploma or certificate?<br />
<select>
<option value="certificate">Certificate</option>
<option>Diploma</option>
<option value="degree">Degree</option>
</select> <br />
<input type="button" class="btnDel" value="Remove Education" disabled="disabled" />
</div>
<div>
<input type="button" id="btnAdd2" value="add Education" />
</div>
<h2>Experience</h2>
<p>Please add all of your experience</p>
<div style="margin-bottom: 4px; class="clonedInput">
Position title: <input type="text"><br /> Years at position:
<input type="number"><br />
Responsibilities: <input type="text"><br />
<input type="text"><br />
Type: <select>
<option>Accounting, banking and Finance</option>
<option>Publishing & Journalism</option>
<option>Social Care & guidance work</option>
</select>
<input type="button" class="btnDel2" value="Remove Experience"
disabled="disabled" />
</div>
<div>
<input type="button" id="btnAdd2" value="add Experience" />
</div>
</form>
Any ideas on how I can fix my script so that when I click the add button for education, a new div containing all of the fields for "education" show up below the previous education box and the same for education?
Any help would be appreciated. Thanks!
Firstly, why do you have 2x $(document).ready? Combine your code into one.
The reason why your duplicated div appear at the end of the form is because both your Education and Experience divs have class="clonedInput", hence $('.clonedInput:last').after(c) causes the duplicated div to be placed after the Experience section (which happens to be the last div that matches the .clonedInput selector).
A solution would to be give each of these sets of divs their own unique class name, such as eduInput and expInput respectively.
The corrected code would hence be:
$('#btnAdd').click(function() {
$('.btnDel:disabled').removeAttr('disabled');
var c = $('.eduInput:first').clone(true);
c.children(':text').attr('name', 'input' + (++inputs));
$('.eduInput:last').after(c);
});
for the education div.
To clean up your code, I suggest binding both Add buttons to the same handler, but act upon them differently by checking the target parameter and determining which set (Education or Experience) to duplicate. Such as:
// single handler and click event for both buttons
var clickHandler = function (e) {
// determine which btnAdd was clicked, such as e.getAttribute('id')
}
$('.btnAdd').click(clickHandler);
But seriously you should clean up your code a little.

Categories

Resources