My HTML code (delivery methods list of radio buttons) is:
<ul id="shipping_method" class="woocommerce-shipping-methods">
<li>
<input type="radio" name="shipping_method[0]" data-index="0" id="shipping_method_0_local_pickup8" value="local_pickup:8" class="shipping_method" checked="checked"><label for="shipping_method_0_local_pickup8">Local pickup</label>
</li>
<li>
<input type="radio" name="shipping_method[0]" data-index="0" id="shipping_method_0_edostavka-package-door-stock7138" value="edostavka-package-door-stock:7:138" class="shipping_method"><label for="shipping_method_0_edostavka-package-door-stock7138">Postamat: <span class="woocommerce-Price-amount amount">265 <span class="woocommerce-Price-currencySymbol">₽</span></span></label>
</li>
<li>
<input type="radio" name="shipping_method[0]" data-index="0" id="shipping_method_0_flat_rate1" value="flat_rate:1" class="shipping_method"><label for="shipping_method_0_flat_rate1">Courier delivery <span class="woocommerce-Price-amount amount">350 <span class="woocommerce-Price-currencySymbol">₽</span></span></label>
</li>
</ul>
It cannot changed (Woocommerce HTML generation). At least I do not want to change it.
My task is add Javascript handlers on selection change event:
jQuery(document).ready(function($){
let methods = jQuery('.shipping_method’);
methods.each(function(index) {
$(this).bind('change', function() {
alert('Change event fired for ' + this.val());
});
});
});
Handlers are not fired when delivery method (radio button with class == ’shipping_method’) changed.
Change handler for whole list of radio also doesn’t work:
methods.bind('change', function() {
alert(this.val());
});
What’s wrong?
Firstly in your JS you're using the wrong type of apostrophe, ’ instead of ', which is unsupported in JS, to close the jQuery selector string.
let methods = jQuery('.shipping_method’); // change the last apostrophe on this line
Secondly, in the change event handler this refers to the Element object which has no val() method. You need to call it on a jQuery object, so use $(this).val() instead.
Lastly, bind() is deprecated and should not be used. Use on() instead, and make sure you're using an up to date version of jQuery, ideally at least something 3.x. You also don't need the each() loop, as you can bind event handlers to a collection of Elements in a single jQuery object. Try this:
jQuery(document).ready(function($) {
$('.shipping_method').on('change', function() {
console.log('Change event fired for ' + $(this).val());
});
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<ul id="shipping_method" class="woocommerce-shipping-methods">
<li>
<input type="radio" name="shipping_method[0]" data-index="0" id="shipping_method_0_local_pickup8" value="local_pickup:8" class="shipping_method" checked="checked"><label for="shipping_method_0_local_pickup8">Local pickup</label>
</li>
<li>
<input type="radio" name="shipping_method[0]" data-index="0" id="shipping_method_0_edostavka-package-door-stock7138" value="edostavka-package-door-stock:7:138" class="shipping_method"><label for="shipping_method_0_edostavka-package-door-stock7138">Postamat: <span class="woocommerce-Price-amount amount">265 <span class="woocommerce-Price-currencySymbol">₽</span></span></label>
</li>
<li>
<input type="radio" name="shipping_method[0]" data-index="0" id="shipping_method_0_flat_rate1" value="flat_rate:1" class="shipping_method"><label for="shipping_method_0_flat_rate1">Courier delivery <span class="woocommerce-Price-amount amount">350 <span class="woocommerce-Price-currencySymbol">₽</span></span></label>
</li>
</ul>
It's worth noting that both of these issues were visible in the console, which is where you should look first when trying to debug JS logic.
#Rory: Awesome! Yes, your advice works great!
Just so you know there's a way to get which shipping method is clicked using a WooCommerce hook:
function prefix_detect_chosen_shipping_method(){
if ( ! defined( 'DOING_AJAX' ) ){
return;
}
$chosen_method = WC()->session->get( 'chosen_shipping_methods' );
if( ! is_array( $chosen_method ) ){
return;
}
$chosen_method = $chosen_method[0];
// Do something with the chosen method.
}
add_action( 'woocommerce_cart_calculate_fees', 'prefix_detect_chosen_shipping_method', 10, 2 );
However, this wouldn't work for me because I needed to fire some javascript based on the selected shipping method. Here's how I accomplished it using some JavaScript that is present only on the checkout page:
(function( $ ) {
'use strict';
$( document ).ready( function(){
$( document.body ).on( 'updated_checkout', () =>{
let prefixShippingMethods = document.querySelectorAll('#shipping_method .shipping_method');
for (const prefixShippingMethod of prefixShippingMethods ){
// We have to check for the hidden type as well because if only one shipping method is available WC doesn't show radio buttons.
if( lpacShippingMethod.checked || lpacShippingMethod.type === 'hidden' ){
// Do something now that you know which method is checked
// NOTE! The shipping method name will not be exact, you will have to use indexOf() for comparisons
if( prefixShippingMethod.value.indexOf('local_pickup') ){
//This will happen when Local Pickup is selected
}
}
}
});
});
})( jQuery );
You can clean up JS as you wish, I couldn't detect this event in vanilla JS, thats why I'm using jQuery here.
Related
I have form with some inputs Adults and child. When I select the values of Adults and child maximum than required and I have logic to display error message. If I change values to less than required (Using onchange) the error message does not go away.
I am able to check while submitting values. My code is working for submit function and having issue while onchange the input of Adults and child. The error message still remains.
Should onchange function inside submit function?
noLink : function() {
$('.nolink').on('click', function(e){
e.preventDefault();
});
},
var $adultsInput = $('#bookingAdultCount'),
$childrenInput = $('#bookingChildrenCount'),
$bookingError = $('#booking-error'),
$bookingForm = $('#bookingWidgetForm'),
$bookingForm.submit(function(event) {
// logic for submit
});
// onchange event that does not work
$adultsInput.on('change', function() {
$adultsInput.parent().removeClass('booking-error');
$childrenInput.parent().removeClass('booking-error');
});
<div class="filter__options-wrapper__filter-option">
<div class="input-container">
<input id="bookingAdultCount" class="initGrow doNotClose" name="Adults" placeholder="Adults" min="1" max="10" readonly>
</div>
<ul class="filter__option__guests-list growMe filter__options-wrapper__single-choice-list open">
<li><a class="nolink" href="#0" data-value="1" data-input-target="bookingAdultCount">1 Adult</a></li>
<li><a class="nolink" href="#0" data-value="2" data-input-target="bookingAdultCount">2 Adults</a></li>
<li><a class="nolink" href="#0" data-value="3" data-input-target="bookingAdultCount">3 Adults</a></li>
<li><a class="nolink" href="#0" data-value="4" data-input-target="bookingAdultCount">4 Adults</a></li>
<li><a class="nolink" href="#0" data-value="5" data-input-target="bookingAdultCount">5 Adults</a></li>
</ul>
</div>
The problem you are having is because change and input events aren't fired when you set the value of an input in JavaScript (which I presume you are doing by way of the <a class="nolink"....>.
Here is some code to demonstrate:
var test = document.getElementById('test');
var btn = document.getElementById('changeval');
//use input instead of change to demonstrate. Input is the same as change except for every character added to the text field.
test.addEventListener('input', (evt) => { console.log(evt.target.value); });
btn.addEventListener('click', (evt) => {
test.value += "a";
});
<input type='text' id='test' value='' />
<button id='changeval'>Change Value</button>
You will have to move your change logic to wherever you are handling the click events for those links.
FYI, The reason for this behavior, I believe, is if you were to change the value of the field in javascript, inside the change event listener for that very field, you would cause an infinite loop. So browser vendors don't expose the change event if the change source was JS (because you can just move all relevant logic to the place where you were setting that value anyhow).
This is driving me crazy. I have tried every different ways to do this. Trying to show/hive a div based on a radio box selection. Below is my code into my main.js :
$("input[name='payment_method']").click(function () {
if ($(this).attr("id") == "payment_method_cod") {
$("#checkout_insurance").show();
} else {
$("#checkout_insurance").hide();
}
});
HTML:
<div id="checkout_insurance">
<h2>Checkout with Insurance</h2>
</div>
<div id="payment" class="woocommerce-checkout-payment">
<ul class="payment_methods methods">
<li class="payment_method_cod">
<input id="payment_method_cod" type="radio" class="input-radio" name="payment_method" value="cod" data-order_button_text="" checked="checked">
<label for="payment_method_cod">
...
</li>
<li class="payment_method_firstdata">
<input id="payment_method_firstdata" type="radio" class="input-radio" name="payment_method" value="firstdata" data-order_button_text="">
<label for="payment_method_firstdata">
....
</li>
</ul>
</div>
I cant get to work. If I place another cdn of the jQuery on the footer, the function works, however messes up a lot of other stuff. jQuery is being loaded on the header. I am current using jQuery 1.11.3.
Any help will be appreciated.
Thank you!
Use :checked selector to check if the radio element with id payment_method_cod is checked or not.
$('#payment_method_cod:checked') will return true/false based on the condition if the radio button is checked or not and toggle it, when true, the element #checkout_insurance will be shown.
Assuming the id's used in the code are unique.
Demo
$(document).on('change', "input[name='payment_method']", function() {
$("#checkout_insurance").toggle($('#payment_method_cod').is(':checked'));
}).trigger('change');
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<div id="checkout_insurance">
<h2>Checkout with Insurance</h2>
</div>
<div id="payment" class="woocommerce-checkout-payment">
<ul class="payment_methods methods">
<li class="payment_method_cod">
<input id="payment_method_cod" type="radio" class="input-radio" name="payment_method" value="cod" data-order_button_text="" checked="checked">
<label for="payment_method_cod">Payment</label>
...
</li>
<li class="payment_method_firstdata">
<input id="payment_method_firstdata" type="radio" class="input-radio" name="payment_method" value="firstdata" data-order_button_text="">
<label for="payment_method_firstdata">FirstData</label>
....
</li>
</ul>
</div>
$("input[name='payment_method']").change(function () {
if ($(this).attr("id") == "payment_method_cod" && $(this).is(':checked')) {
alert('show');
} else {
alert('hide');
}
});
Please use .change() on checkbox use .click() on button.
Try this approach this will select change event on checkbox and check if the id of the checkbox is payment_method_cod it will then show or hide depending on checked property
demo
Changing your code a bit :
$("input[name='payment_method']").click(function () {
var _this = $(this);
if (_this.val() == "cod") {
$("#checkout_insurance").show();
} else {
$("#checkout_insurance").hide();
}
});
I have using two anchor tag to transfer the control to same page. and gives two hidden input with same id but different values. as shown in given code.
<li data-icon="false"><a href="#paymentReceiptVoucher" onclick="loadAccForPayVoucher();">
<input type="hidden" id="PRVou" value="payment">PaymentReceipt Voucher</a></li>
<li data-icon="false"><a href="#paymentReceiptVoucher" onclick="loadAccForPayVoucher();">
<input type="hidden" id="PRVou" value="receipt">ReceiptPayment Voucher</a></li>
and i want to get the values of these hidden tags by using following javascript code.
loadAccForPayVoucher = function() {
alert(document.getElementById('PRVou').value);
}
and it always alert payment. how can i get the value according to the link. Thanks.
id attributes are meant to be unique, regardless of their context. There should only be one element in the entire document with a given id.
Give the element a class name instead:
<input type="hidden" class="PRVou" value="payment">
And then use getElementsByClassName:
document.getElementsByClassName('PRVou')[0].value
Try this:
<li data-icon="false"> <a href="#paymentReceiptVoucher" onclick="loadAccForPayVoucher('payment');">
<!--<input type="hidden" id="PRVou" value="payment">PaymentReceipt Voucher</a> --></li>
<li data-icon="false"><a href="#paymentReceiptVoucher" onclick="loadAccForPayVoucher('receipt');">
<!-- <input type="hidden" id="PRVou" value="receipt">ReceiptPayment Voucher</a> --></li>
<script type="text/javascript">
loadAccForPayVoucher = function(type) {
alert(type);
//alert(document.getElementById('PRVou').value);
}
</script>
In HTML the ID attributes are always unique, thus when you call document.getElementById, the DOM of the browser will go out and fetch any (most likely the first) element with the given ID.
What can I do?
Give them separate IDs, your html will look like this:
<li data-icon="false"><a href="#paymentReceiptVoucher" onclick="loadAccForPayVoucherPayment();">
<input type="hidden" id="PRVou-payment" value="payment"/>PaymentReceipt Voucher</a></li>
<li data-icon="false"><a href="#paymentReceiptVoucher" onclick="loadAccForPayVoucherReceipt();">
<input type="hidden" id="PRVou-receipt" value="receipt"/>ReceiptPayment Voucher</a></li>
And then your JavaScript will have separate event handlers:
loadAccForPayVoucherPayment = function() {
alert(document.getElementById('PRVou-payment').value);
}
loadAccForPayVoucherReceipt = function() {
alert(document.getElementById('PRVou-receipt').value);
}
Update: I made you a fiddle :) http://jsfiddle.net/YCXC8/
let's say i am having fallowing html structure-
<div data-theme="a" class="ui-btn ui-btn-icon-right ui-btn-corner-all ui-shadow ui-btn-up-a">
<span class="ui-btn-inner ui-btn-corner-all">
<span class="ui-btn-text">Select One</span>
<span class="ui-icon ui-icon-arrow-d ui-icon-shadow">
</span>
</span>
<select onchange="selectState(this,'ADDR_SHIP_STATE')" id="ADDR_SHIP_STATE" name="ADDR_SHIP_STATE">
<option>Hi</option>
<option>Hello</option>
<option>How</option>
<option>are</option>
<option>you</option>
</select>
</div>
what i am trying to do is on change of select box, take the text of selected option and place it inside span(replace select one with this data) having class name ui-btn-text
below is the code which i ahve tried so for without any luck
function selectState(id,stateId){
var selectedState = $("#"+stateId+" option:selected").text();
$(id).closest("ui-btn-text").text(selectedState);
}
Please suggest me how can i do this..
.closest()ref will progress up the DOM but won't get down like .parents() does (btw, you didn't add the . (dot) for the class search but this is probably a typo)
Here is a try:
$(id).parent().find(".ui-btn-text").text(selectedState);
Try -
function selectState(id, stateId) {
var selectedState = $("#" + stateId + " option:selected").val();
$(id).parents('div').find(".ui-btn-text").text(selectedState);
}
This will -
Find the parent 'div' of the 'select' element
Use find to get the .ui-btn-text element contained in the parent div
Demo - http://jsfiddle.net/H2pea/1
Using attributes like onchange is not really unobtrusive way if you use jQuery. This is better:
$('#ADDR_SHIP_STATE').change(function() {
$(this).prev().find('.ui-btn-text').text($(this).val());
});
I am using the teleriks treeview control (asp.net mvc extensions), where I may have up to three children nodes, like so (drumroll...... awesome diagram below):
it has its own formatting, looking a bit like this:
<%=
Html.Telerik().TreeView()
.Name("TreeView")
.BindTo(Model, mappings =>
{
mappings.For<Node1>(binding => binding
.ItemDataBound((item, Node1) =>
{
item.Text = Node1.Property1;
item.Value = Node1.ID.ToString();
})
.Children(Node1 => Node1.AssocProperty));
mappings.For<Node2>(binding => binding
.ItemDataBound((item, Node2) =>
{
item.Text = Node2.Property1;
item.Value = Node2.ID.ToString();
})
.Children(Node2 => Node2.AssocProperty));
mappings.For<Node3>(binding => binding
.ItemDataBound((item, Node3) =>
{
item.Text = Node3.Property1;
item.Value = Node3.ID.ToString();
}));
})
%>
which causes it to render like this. I find it unsual that when I set the value it is rendered in a hidden input ? But anyway:...
<li class="t-item">
<div class="t-mid">
<span class="t-icon t-plus"></span>
<span class="t-in">Node 1</span>
<input class="t-input" name="itemValue" type="hidden" value="6" /></div>
<ul class="t-group" style="display:none">
<li class="t-item t-last">
<div class="t-top t-bot">
<span class="t-icon t-plus"></span>
<span class="t-in">Node 1.1</span>
<input class="t-input" name="itemValue" type="hidden" value="207" />
</div>
<ul class="t-group" style="display:none">
<li class="t-item">
<div class="t-top">
<span class="t-in">Node 1.1.1</span>
<input class="t-input" name="itemValue" type="hidden" value="1452" />
</div>
</li>
<li class="t-item t-last">
<div class="t-bot">
<span class="t-in">Node 1.1.2</span>
<input class="t-input" name="itemValue" type="hidden" value="1453" />
</div>
</li>
</ul>
</li>
</ul>
What I am doing is updating a div after the user clicks on a certain node. But when the user clicks on a node, I want to send the ID not the Node text property. Which means I have to get it out of the value in these type lines <input class="t-input" name="itemValue" type="hidden" value="1453" />, but it can be nested differently each time, so the existing code I ahve doesn't ALWAYS work:
<script type="text/javascript">
function TreeView_onSelect(e) {
//`this` is the DOM element of the treeview
var treeview = $(this).data('tTreeView');
var nodeElement = e.item;
var id = e.item.children[0].children[2].value;
...
</script>
So based on that, what is a better way to get the appropriate id each time with javascript/jquery?
edit:
Sorry to clarify a few things
1) Yes, I am handling clicks to the lis of the tree & want to find the value of the nested hidden input field. As you can see, from the telerik code, setting item.Value = Node2.ID.ToString(); caused it to render in a hidden input field.
I am responding to clicks anywhere in the tree, therefore I cannot use my existing code, which relied on a set relationship (it would work for first nodes (Node 1) not for anything nested below)
What I want is, whenever there is something like this, representing a node, which is then clicked:
<li class="t-item t-last">
<div class="t-bot">
<span class="t-in">Node 1.1.2</span>
<input class="t-input" name="itemValue" type="hidden" value="1453" />
</div>
</li>
I want the ID value out of the input, in this case 1453.
Hope this now makes a lot more sense.
if possible would love to extend this to also store in a variable how nested the element that is clicked is, i.e. if Node 1.1.2 is clicked return 2, Node 1.1 return 1 and node 1 returns 0
It's a little unclear what you're asking, but based on your snippet of JavaScript, I'm guessing that you're handling clicks to the lis of the tree & want to find the value of the nested hidden field? If so, you want something like this:
function TreeView_onSelect(e) {
var id = $(e.item).find(".t-input:first").val();
}
Edit: In answer to your follow-up question, you should be able to get the tree depth with the following:
var depth = $(e.item).parents(".t-item").length;
In jQuery you can return any form element value using .val();
$(this).val(); // would return value of the 'this' element.
I'm not sure why you are using the same hidden input field name "itemValue", but if you can give a little more clarity about what you are asking I'm sure it's not too difficult.
$('.t-input').live('change',function(){
var ID_in_question=$(this).val();
});