Is it possible to change the Add to cart button in Woocommerce to "Select size", for example, if a customer has yet to choose an (required) option. Then, change it back to "add to cart" once the option is selected?
The Website of manolo blahnik has this feature, and I find it's a good UX for customers. On their website, the first stage is "Add to cart".
If someone hovers over the button, but hasn't chosen their option, it displays "Please select a size".
But if someone has choosen an option, it stays as "Add to cart".
On Mobile, because there is no hover like on pc, it always shows "Please select a size" until the option is chosen, then it changes to "Add to cart".
I'm wondering if it is possible to do this with woocommerce.
You can use mouseover and mouseout event handlers on the Add to cart button. The following code snippet demonstrates the functionality.
On mouseover of the button, the values of the required form fields are checked and the button text and title are amended using the data-warn attribute of the form field.
On mouseout the button text and title are reset using the content of the data-title attribute on the button.
// handle to button
var addToCartBtn = document.getElementById('add-to-cart-button');
// 'mouseover' event handler
addToCartBtn.addEventListener('mouseover', function(event) {
// get all form elements
var inputs = this.form.elements;
for (var i = 0; i < inputs.length; i++) {
// find the first empty required field
if (inputs[i].required && inputs[i].value == '') {
// set the button's mouse over text
event.target.title = inputs[i].dataset.warn;
// set the button's text
event.target.innerHTML = inputs[i].dataset.warn;
break;
}
}
});
// 'mouseout' event handler
addToCartBtn.addEventListener('mouseout', function(event) {
event.target.innerHTML = event.target.dataset.title;
event.target.title = event.target.dataset.title;
});
.form-field {
margin: 20px;
}
#add-to-cart-button {
font-size: 18px;
width: 200px;
height: 50px;
}
<form action="#" name="add-to-cart-form">
<div class="form-field">
<label>
<span>Select Size:</span>
<select name="item-size" data-warn="Please choose a size" required>
<option value="">Choose a size</option>
<option value="s">Small</option>
<option value="m">Medium</option>
<option value="l">Large</option>
</select>
</label>
</div>
<div class="form-field">
<label>
<span>Select Colour:</span>
<select name="item-colour" data-warn="Please choose a colour" required>
<option value="">Choose a colour</option>
<option value="red">Red</option>
<option value="green">Green</option>
<option value="blue">Blue</option>
</select>
</label>
</div>
<div class="form-field">
<button type="submit" name="add-to-cart-button" id="add-to-cart-button" title="Add to Basket" data-title="Add to Cart">Add to Basket</button>
</div>
</form>
Yes, it is possible to change the text on hover. You could do something like this;
#cart-btn {
width: 200px;
background: #000;
color: #fff;
}
#cart-btn:after{
content:'ADD TO BAG';
}
#cart-btn:hover:after{
content:'PLEASE SELECT SIZE';
}
<button id="cart-btn" type="button"></button>
This is just the CSS part of how you can get the result
.textSwap::before {
content: "Add to Cart";
}
.textSwap:hover::before {
content: "Please Select";
}
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-beta.3/css/bootstrap.min.css" rel="stylesheet"/>
<style>
.textSwap
{
min-width:200px;
}
.textSwap::before {
content: "Add to Cart";
}
.textSwap:hover::before {
content: "Please Select";
}
</style>
<button type="button" class="btn btn-secondary btn-lg textSwap"></button>
Related
I'm trying to find a way to display a pop-up alert if the customer did not select installation while checking out their product. So on the checkout page, if it doesn't say the word "Install" on it - is it possible to display a nice alert popup that says something like "Are you sure you don't want installation?".
I found a script that I don't really know how to edit and I would like to only display the alert on the checkout page, and I don't have access to add code to just the checkout page, only the global script file.
Snippet of code:
window.onload = function() {
// If the body element of the page contains 'one hour turnaround' then trigger an alert
if (document.body.innerHTML.toString().indexOf('one hour turnaround') > -1) {
alert("You have a ONE HOUR TURNAROUND order");
}
};
<div class="form-group ">
<label class="control-label" for="input-option90049">
<div style="font-size: 1.7em;border: 1px solid #e6db55; background-color: #FFFFE0; padding: 10px;"><i class="fa fa-wrench faa-wrench animated fa-2x" aria-hidden="true"></i> Would you like Professional Installation?</div>
</label>
<select name="option[90049]" id="input-option90049" class="form-control" data-toggle-option-select="true">
<option value=""> --- Please Select --- </option>
<option value="179975">Yes - I need installation (+$95.00)</option>
<option value="179976">No - I do not need installation</option>
</select>
</div>
<script type="text/javascript">
window.onload = function() {
// If the body element of the page contains 'one hour turnaround' then trigger an alert
if (document.body.innerHTML.toString().indexOf('one hour turnaround') > -1) {
alert("You have a ONE HOUR TURNAROUND order");
}
};
</script>
This is my HTML to the form on the product page to select if you would want installation or not...
<div class="form-group ">
<label class="control-label" for="input-option90049">
<div style="font-size: 1.7em;border: 1px solid #e6db55; background-color: #FFFFE0; padding: 10px;"><i class="fa fa-wrench faa-wrench animated fa-2x" aria-hidden="true"></i> Would you like Professional Installation?</div>
</label>
<select name="option[90049]" id="input-option90049" class="form-control" data-toggle-option-select="true">
<option value=""> --- Please Select --- </option>
<option value="179975">Yes - I need installation (+$95.00)</option>
<option value="179976">No - I do not need installation</option>
</select>
</div>
This does not directly answer the question but given the actual use, I would suggest a verification - which can be triggered on load or select change. Here is a very simplistic example.
I added a more standard way of doing a validation and triggered that when the page loads/script runs.
function validateInstallation(event) {
let installSelect = this;
let optIndex = installSelect.selectedIndex
let installSelectedValue = installSelect.options[optIndex].value;
console.log("Value:", installSelectedValue, "Index:", optIndex);
/* this could be in a validation message etc. also */
if (optIndex == 0 || installSelectedValue == "") {
console.log("Please select an installation option");
} else {
console.log("Selected:", installSelectedValue);
}
}
let optionSelector = '#input-option90049';
let installOptionElem = document.querySelector(optionSelector);
// add a selectyionchange event listener and trigger that when we load
installOptionElem.addEventListener('change', validateInstallation);
const event = new Event('change');
installOptionElem.dispatchEvent(event);
<div class="form-group ">
<label class="control-label" for="input-option90049">
<div style="font-size: 1.7em;border: 1px solid #e6db55; background-color: #FFFFE0; padding: 10px;"><i class="fa fa-wrench faa-wrench animated fa-2x" aria-hidden="true"></i> Would you like Professional Installation?</div>
</label>
<select name="option[90049]" id="input-option90049" class="form-control" data-toggle-option-select="true">
<option value=""> --- Please Select --- </option>
<option value="179975">Yes - I need installation (+$95.00)</option>
<option value="179976">No - I do not need installation</option>
</select>
</div>
I am editing an existing form with inputs that are hidden until the user clicks on an option, and then text input will appear. I need to get that specific text input to have active focus once that happens. I would love to do this with CSS because I am not great with javascript, but here is a portion of that code:
if ($("#frmSubmittedValue").val() == '') {
return false;
}
if ($("#frmSubmittedValue").val() == 'Individual')
$("#personSubmittedByValue").val('');
if ($("#frmProjectSub").val() == 0) {
return false;
}
Any help is greatly appreciated!
Who's afraid of some javascript? I've thrown in a little extra method select()
function show_it() {
document.querySelector("#f16").style.display = "block";
}
function focus_it() {
document.querySelector("#f16").focus();
}
function select_it() {
document.querySelector("#f16").select();
}
<button onclick="show_it(); focus_it(); select_it();">click</button>
<input id="f16" type="text" value="untitled" style="display:none">
You can set focus with jQuery using the code below:
$( "#other" ).click(function() {
$( "#target" ).focus();
});
There's a simple form example on the jQuery site here.
As for making the element visible, start off having the element you don't want shown to be display: none and has aria-hidden="true" (so screen-readers avoid) and a tabIndex="-1" (so the user can't tab to it). Then, using an if condition, set the properties to display: block, aria-hidden="false" and tabIndex="0".
I've put together a rough form with a few different input types. It's not perfect, but should give the general idea.
function makeMeVisible(id) {
$(id).parent().removeClass('hidden').attr('aria-hidden', 'false').attr('tabIndex', '0');
}
function makeMeVanish(id) {
$(id).parent().addClass('hidden').attr('aria-hidden', 'true').attr('tabIndex', '-1')
}
/* Select the first input and listen out for when it gets clicked */
$('#itemOne').on('click', function() {
/* If we are checked, run the makeMeVisible function and set focus to the next element we want shown */
if( $(this).is(':checked') ) {
makeMeVisible('#itemTwo');
$('#itemTwo').focus();
} else {
/* If we're not, make the item vanish and set the focus back to the original element */
makeMeVanish('#itemTwo');
$('#itemOne').focus();
}
})
/* Select elements need to listen for a change. When changes, get the selected option */
$('#itemTwo').on('change', function() {
/* Change what we do next based on what is chosen (looking at the value property) */
switch($('#itemTwo option:selected').val()) {
case "Value 1" :
makeMeVanish('#itemFive');
makeMeVisible('#itemFour');
$('#itemFour').focus();
break;
case "Value 2" :
makeMeVanish('#itemFour');
makeMeVisible('#itemFive');
$('#itemFive').focus();
break;
default:
/* If nothing is chosen, hide both */
makeMeVanish('#itemFour');
makeMeVanish('#itemFive');
}
})
.form-item + .form-item {
margin-top: 1rem;
}
.form-item {
display: block;
}
.hidden {
display: none;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<form>
<div class="form-item"><label><input type="checkbox" id="itemOne">Item one</label></div>
<div class="form-item hidden" aria-hidden="true" tabIndex="-1">
<label for="itemTwo">Item two</label>
<select name="option" id="itemTwo">
<option>Select</option>
<option value="Value 1">Option one</option>
<option value="Value 2">Option two</option>
</select>
</div>
<div class="form-item hidden" aria-hidden="true" tabIndex="-1">
<label for="itemThree">Item three</label>
<input type="text" id="itemThree">
</div>
<div class="form-item hidden" aria-hidden="true" tabIndex="-1">
<label for="itemFour">Item four (Option one)</label>
<input type="text" id="itemFour">
</div>
<div class="form-item hidden" aria-hidden="true" tabIndex="-1">
<label for="itemFive">Item five (Option two)</label>
<input type="text" id="itemFive">
</div>
</form>
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.)
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>
This question already has answers here:
jQuery select change show/hide div event
(10 answers)
Closed 8 years ago.
I have a select box with 3 option body, strength, distance. I want to use javascript / jquery to show a div depending on which of those options is selected. so far I have just got it working on a button push http://codepen.io/Irish1/pen/iwKam
html
<form>
<p>
<label for="select">
Goal Type
</label>
<select class="typeselect">
<option value=""></option>
<option value="body">
Body
</option>
<option value="strength">
Strength
</option>
<option value="distance">
Distance
</option>
</select>
<button class="toggle">push</button>
</p>
<div class="body">
<p>
<label for="body">
Body
</label>
</p>
</div>
<div class="strength">
<p>
<label for="strength">
Strength
</label>
</p>
</div>
<div class="distance">
<p>
<label for="distance">
Distance
</label>
</p>
</div>
</form>
css
.body {
display: none;
}
.strength {
display: none;
}
.distance {
display: none;
}
javascript
$('.toggle').click(function(){
$('.body').toggle();
});
Try
//dom ready handler
jQuery(function () {
//all the elements that has to be shown/hidden, it is cached for easy access later
var $targets = $('.body, .strength, .distance');
//chang handler for the select element
$('.typeselect').change(function () {
//hide previously displayed elements
$targets.hide()
//find the element with the selected option's value as class, if empty option is selected set it to an empty set
if (this.value) {
$('.' + this.value).show()
}
})
})
Demo: Fiddle
If you want to make the button handler work then
//dom ready handler
jQuery(function () {
//all the elements that has to be shown/hidden, it is cached for easy access later
var $targets = $('.body, .strength, .distance');
var $select = $('.typeselect');
//click handler for the button
$('.toggle').click(function (e) {
//prevent the default form action of the button click
e.preventDefault();
//hide previously displayed elements
$targets.hide()
//selected value
var val = $select.val();
//find the element with the selected option's value as class, if empty option is selected set it to an empty set
if (val) {
$('.' + val).show()
}
})
})
Demo: Fiddle
$('#typeselect').change(function(event) {
$('.target').hide();
var option = $(this).val();
if (option != "") $('.'+option).show();
});
and a few minor mods to the html
<form>
<p>
<label for="select">Goal Type</label>
<select id="typeselect" class="typeselect">
<option value=""></option>
<option value="body">Body</option>
<option value="strength">Strength</option>
<option value="distance">Distance</option>
</select>
</p>
<div class="body target">
<p><label for="body">Body</label></p>
</div>
<div class="strength target">
<p><label for="strength">Strength</label></p>
</div>
<div class="distance target">
<p><label for="distance">Distance</label></p>
</div>
</form>
You can view and edit here at codepen: http://codepen.io/anon/pen/ktesh
Give a name / Id to Select and Div's.
var x = document.getElementById("SelectId / Name").selected;
if(x==0) {
document.getElementById("body").style.display = "block";
} else {
\\ Similarly for other div to display on the basis of x
}
Try this:
$('.typeselect').change(function(){
$('div').hide();
if($(this).val() == ""){
$(this).children('option:first-child').show();
}
else
$('.' + $(this).val()).show();
});