I cannot get a nested variable value to populate outside a nested function within a button's click event function even though I believe I am using a global variable.
What am I doing wrong to pull value into a console log outside the nested function?
I am creating a shopping cart utilizing jquery pop-up with ajax and php. I am able to add items to the cart as well as add a name & email input field.
When I go to console log in Chrome for the focusout event for the fields they show the values but when trying to use a Checkout button, I am not able to pass the data within the Checkout click outside of a nested function even with a global variable.
--JS--
var formname;
$(document).ready(function() {
...
$(document).on('click', '#check_out_cart', function(){
$('#cart-popover').popover({
html : true,
container: 'body',
content:function(){
return $('#popover_content_wrapper').html();
}
});
$(document).on('click', '#cart-popover', function(){
$('input#namef').focus();
});
$('input#namef').on('focusout', function(){
formname= $('input#namef').val();
console.log(formname);
});
var scart_add = $("input[name='scart_add']").val();
console.log("Scart value is "+scart_add);
console.log("Name is "+formname);
...
});
});
--HTML--
<div class="container">
<nav class="navbar navbar-default" role="navigation">
<div class="container-fluid">
<div class="navbar-header">
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target=".navbar-collapse">
<span class="sr-only">Menu</span>
<span class="glyphicon glyphicon-menu-hamburger"></span>
</button>
</div>
<div id="navbar-cart" class="navbar-collapse collapse">
<ul class="nav navbar-nav">
<li>
<a id="cart-popover" class="btn" data-placement="bottom" title="Shopping Cart">
<span class="glyphicon glyphicon-shopping-cart-2x"></span>
<span class="badge"></span>
<span class="total_price">$ 0.00</span>
</a>
</li>
</ul>
</div>
</div>
</nav>
<div id="popover_content_wrapper" style="display: none">
<span id="cart_details"></span>
<div>
<form method="POST">
Name: <input type="text" id="namef" >
Email: <input type="text" id="emailf" >
<input type="hidden" name="scart_add" value="1" /><br><br>
</div>
<div align="right">
<button type="button" class="btn btn-primary" id="check_out_cart">
<span class="glyphicon glyphicon-shopping-cart"></span> Check out
</button>
<button type="button" class="btn btn-default" id="clear_cart">
<span class="glyphicon glyphicon-trash"></span> Clear
</button>
</div>
</form>
</div>
</div>
<div id="display_item">
</div>
...
</div>
I am expecting the value from the input#namef text to appear in the console.log ...formname variable but it just shows as "".
The event for focusout isn't added to the input until you click the button:
Also, I don't know if it is how you copied and pasted your HTML and Javascript, but it was throwing errors when I put it into a fiddle.
Move this outside of the button click handler as Rory pointed out:
var formname;
$(document).ready(function() {
//Now focusout handler is added on DOM Ready instead of when you click the button
$('input#namef').on('focusout', function(){
formname= $('input#namef').val();
console.log(formname);
});
$(document).on('click', '#check_out_cart', function(){
var scart_add = $("input[name='scart_add']").val();
console.log("Scart value is "+scart_add);
console.log("Name is "+formname);
});
});
Here is a working fiddle: https://jsfiddle.net/hqgv7zsa/
This is a race condition, of sorts.
On document.ready event, you are setting a click event handler
Inside the click handler, you are setting the focusout event handler
Since the click handler is still executing,
immediately, the code runs to show value of scart and formname.
At that time, formname is still empty because the current function
(click event handler) is still executing, and the focusout event, even
if it fires, will fire after that code is executed.
You should move the focusout handler declaration code outside of the click handler code, which will then set both handlers on document.ready() event.
Here's a breakdown of what's happening and when:
var formname;
$(document).ready(function() {
This fires when DOM is loaded (document.ready event)
$(document).on('click', '#check_out_cart', function(){
This fires when user clicks on some element with ID check_out_cart
$('input#namef').on('focusout', function(){
This event handler is only being set once user clicks on cart, but it is not yet executed!
formname= $('input#namef').val();
console.log(formname);
});
This code fires once the event handler has been set for focusout, but still has not executed. Even if a focusout event is fired, it will only execute when the current function exits.
var scart_add = $("input[name='scart_add']").val();
console.log("Scart value is "+scart_add);
Correctly, you will see that formname is empty at this point.
console.log("Name is "+formname);
...
});
});
Hope this explains the flow you're seeing;
Quick fix as in Ryan Wilson's answer, move the focusout handler declaration to the scope of the document.ready handler, outside the click handler.
Using the solution from Unable to fetch values from an input element in Bootstrap's popover, I was able to adjust my JS code to fetch the values with a .find within the .popover-content class that was added by the popover JS.
var formname;
var formemail;
$(document).ready(function() {
//shopping cart
/*load functions*/
load_product();
load_cart_data();
$('#cart-popover').popover({
html : true,
container: 'body',
content:function(){
return $('#popover_content_wrapper').html();
}
});
$(document).on('click', '#cart-popover', function(){
$('input#namef').focus();
});
$(document).on('focusout', '#namef', function(){
formname = $('.popover-content').find('#namef').val();
});
$(document).on('focusout', '#emailf', function(){
formemail = $('.popover-content').find('#emailf').val();
});
$(document).on('click', '#check_out_cart', function(){
var scart_add = $("input[name='scart_add']").val();
var nameval = $("input#namef").val();
alert("Scart value is "+scart_add);
alert("Name is "+formname);
alert("Email is "+formemail);
});
});
Related
I am using Bootstrap 5.2 and I have two buttons that can hide content, using the Bootstrap collapse plugin.
<div class="col-12 col-sm-auto">
<span class="pe-2">
<button id="show_movements_button" type="btn" class="btn btn-outline-primary" data-bs-toggle="collapse" data-bs-target="#movements_id">
Show Movements
</button>
</span>
<span class="pe-2">
<button id="show_credits_button" type="btn" class="btn btn-outline-secondary" data-bs-toggle="collapse" data-bs-target="#credits_id">
Show All Credits
</button>
</span>
</div>
such as
<tr class="song_id collapse" id="movements_id">
<td class="col-1">
1
</td>
<td class="col">
</td>
<td class="col">
<div>
<label class="h6">
Piano Concerto no. 1 in E minor, op. 11: I. Allegro maestoso
</label>
</div>
<div class="collapse" id="credits_id">
<div class="lh-1">
<div>
<a href="/container.start?cid=0$=Instrument$708&title=Instruments+%2F+piano" class="small text-secondary">
piano
</a>
<label class="small">
by
</label>
<a href="/container.start?cid=0$=Performer_name$5540&title=Performers+%2F+Evgeny+Kissin" class="small text-secondary pe-1">
Evgeny Kissin
</a>
</div>
</div>
</div>
</td>
</tr>
This work correctly, but I want the name of the button to change to indicate if showing or hiding content so I also have this code
<script>
function listenForButtonCollapse(buttonId, collapseId, buttonShowText, buttonHideText)
{
let button = document.getElementById(buttonId);
let section = document.getElementById(collapseId);
if(section!=null)
{
section.addEventListener('show.bs.collapse',
function()
{
button.innerText=buttonHideText;
}
);
section.addEventListener('hide.bs.collapse',
function()
{
button.innerText=buttonShowText;
}
);
}
}
</script>
<script>
listenForButtonCollapse('show_credits_button','credits_id','Show All Credits','Hide Some Credits');
</script>
<script>
listenForButtonCollapse('show_movements_button','movements_id','Show Movements','Hide Movements');
</script>
Now toggling the Show/Hide Movements button works fine, but when I click on the Show/Hide Credits button for some reason it is also triggering the listenForButtonCollapse() call on the movements button as well as the credits button, so the Movement button is updated with the same (Hide/Show) value as the credits button even though it isn't actually been invoked (so it doesn't hide/show the movements div)
The credits div is within the movements div, so Im assuming that why one button works without problem and the other doesn't but I cant see what I am actually doing wrong.
In order to avoid the current behaviour you need to stop further event propagation. You can do this by using stopPropagation(). This method of the Event interface prevents further propagation of the current event in the capturing and bubbling phases.
function listenForButtonCollapse(buttonId, collapseId, buttonShowText, buttonHideText)
{
let button = document.getElementById(buttonId);
let section = document.getElementById(collapseId);
if (section != null)
{
section.addEventListener('show.bs.collapse',
function(event)
{
event.stopPropagation();
button.innerText=buttonHideText;
}
);
section.addEventListener('hide.bs.collapse',
function(event)
{
event.stopPropagation();
button.innerText=buttonShowText;
}
);
}
}
I've prepared a Code playground to illustrate that this solves your issue:
https://codesandbox.io/embed/bootstrap-5-playground-forked-b28j0g?fontsize=14&hidenavigation=1&theme=dark
Solution Explanation
The events show.bs.collapse and hide.bs.collapse are being triggered for both buttons when clicked. So when you add more than one event listener for those events, when the event occurs, all event listeners are being executed. This is why for example when you click "Hide Movements" and bootstrap triggers hide.bs.collapse
event then this executes all registered event listeners for it - in this case two - one for the show_credits_button and another for show_movements_button button - leading to changing both button texts. To prevent this you need to stop further event propagation with event.stopPropagation() method - this will stop notifying all other event listeners for this event other than the event target one.
I have a very simple script, that after clicking a span changes the opacity of another element.
But for some reason, I have to click it two times.
I heard it could be something with whitespace, but I still cannot solve it.
Here's the element:
<div class="popup">
<span class="popup__close" onclick="hidePopup()"><i class="fas fa-lg fa-times"></i></span>
<h3 class="popup__heading">
Check our newsletter!
</h3>
<p class="popup__text u-margin-bottom-xs">
Register to our newsletter to see the newest offers!
</p>
Register
</div>
And here's the script:
<script>
function hidePopup() {
const close = document.querySelector('.popup__close');
const popup = document.querySelector('.popup');
close.addEventListener('click', function() {
popup.style.opacity = '0';
})
}
</script>
You need to double click as you are attaching event handler using addEventListener() in the inline click handler.
I would recommend you to use DOMContentLoaded event and soley attach event handler using addEventListener()
window.addEventListener('DOMContentLoaded', (event) => {
console.log('DOM fully loaded and parsed');
const close = document.querySelector('.popup__close');
const popup = document.querySelector('.popup');
close.addEventListener('click', function() {
popup.style.opacity = '0';
});
});
And there is no need for ugly inline click handler and the function hidePopup(), remove them
Remove the extraneous call to addEventListener and simply hide the element in the click event handler.
function hidePopup() {
const close = document.querySelector('.popup__close');
const popup = document.querySelector('.popup');
popup.style.opacity = '0';
}
in the function hidePopup, notice that you "addEventListener" when the function runs 1st time,
when you addEventListener, doesn't make the function run!
you assign the listener so that later if clicked, then it will run the function you assigned in the Listener.
if you use addEventListener, you dont need to use onClick attribute to call the function anymore...
I see that your goal is to hide the content when close button is clicked, by following your code, there are 2 ways for that:
1st way
//you only need to select once
const popup = document.querySelector('.popup');
function hidePopup() {
popup.style.opacity = '0';
}
//additional function if you want to show pop up
function showPopup() {
popup.style.opacity = '1';
}
<div class="popup">
<span class="popup__close" onclick="hidePopup()">
<i class="fas fa-lg fa-times">X</i>
</span>
<h3 class="popup__heading">
Check our newsletter!
</h3>
<p class="popup__text u-margin-bottom-xs">
Register to our newsletter to see the newest offers!
</p>
Register
</div>
<Br><Br><br>
<span class="popup__show" onclick="showPopup()">
click me to show pop up
</span>
2nd Way
//select both button and Popup
const popup = document.querySelector('.popup');
const close = document.querySelector('.popup__close');
close.addEventListener('click', function() {
popup.style.opacity = '0';
});
const show = document.querySelector('.popup__show');
show.addEventListener('click', function() {
popup.style.opacity = '1';
});
<div class="popup">
<span class="popup__close">
<i class="fas fa-lg fa-times">X</i>
</span>
<h3 class="popup__heading"https://stackoverflow.com/questions/63126988/why-do-i-need-to-click-two-times-to-trigger-an-onclick-function#>
Check our newsletter!
</h3>
<p class="popup__text u-margin-bottom-xs">
Register to our newsletter to see the newest offers!
</p>
Register
</div>
<span class="popup__show">Click here to show pop up</span>
I have some buttons and I added some behavior on click to all of them with jQuery and they worked fine, but now that those buttons are generated dynamically by changing the innerHTML of the div with a script, the behavior don't work anymore
Here is an example, like this every time I click any of the two buttons, it show an alert with the message 'clicked'.
Fiddle
$('.foo').on('click', function(){
alert('clicked');
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="home">
<button class='foo' > Test </button>
<button class='foo' > Test </button>
</div>
But if I insert the buttons by changing the innerHTML of home with the button generate, it does not works anymore
Fiddle
$(".gen").on("click", function(){
$('.home').html(
"<button class='foo' > Test </button>" +
"<button class='foo' > Test </button>");
})
$('.foo').on('click', function(){
alert('clicked');
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div>
<button class="gen" > Generate </button>
</div>
<div class="home">
</div>
I really don't know what's going on
$('.foo') selects certain elements. .on() adds an event handler to only the selected elements. Any new elements with the "foo" class will not have that handler. Either add them manually to the new elements or better still use a delegate.
Basically, since your "foo" elements do not exist until after you click "generate", the call to .on() adds a handler to nothing.
Here's a solution using jQuery's delegate implementation
$(".gen").on("click", function(){
$('.home').html(
"<button class='foo' > Test </button>" +
"<button class='foo' > Test </button>");
})
$(document).on('click', '.foo', function(){
console.log('clicked');
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div>
<button class="gen" > Generate </button>
</div>
<div class="home">
</div>
I am trying to create a jquery to click a hyperlink but nothing seems to be working.
HTML
<main id="main" class="main-content">
<div class="container">
<div class="warning" role="alert">
no avail
Show all
</div>
what I was trying
$(".warning a").click()
Any suggestions?
Note that jQuery-initiated "click" events will fire the event but will not cause navigation to occur.
Instead you can read the link's HREF attribute and directly set the window location:
// The click event:
$('a').on("click", function() {
console.log("Click event fired");
})
var demo1 = function() {
// This will trigger the click event, but will not navigate.
$(".warning a").click()
}
var demo2 = function() {
// This will navigate but will not trigger the click event. (If you need both to happen, trigger the click event first, and consider delaying the window location update if necessary.)
var url = $(".warning a").attr("href")
window.location = url;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<main id="main" class="main-content">
<div class="container">
<div class="warning" role="alert">
Show all
</div>
</div>
</main>
<!-- for demo: -->
<button onclick="demo1()">Won't work</button>
<button onclick="demo2()">Will work</button>
jQuery's .click() (without arguments) is a shortcut for .trigger("click"):
function(a,c) {
return arguments.length > 0 ? this.on(b, null, a, c) : this.trigger(b)
}
Therefore, it will not actually click the element, but just call the click event handlers attached to it, as you can see here:
const $link = $("a");
$link.on("click", () => {
console.log("Clicked? Not really...");
});
$link.click();
$link.trigger("click");
Show all
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
You need to get a reference to the actual DOM element and then call HTMLElement.click() on that:
$("a")[0].click();
Show all
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
You can user the vanilla click method:
document.querySelector('.warning > a').click()
// equivalent jquery
//$('.warning > a')[0].click()
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="warning" role="alert">
no avail
Show all
</div>
Whenever you select div class with hyperlink there you get array because you can have multiple hyperlinks so you need to add somthing like below
Code
$('.warning a')[0].click();
For reference link
Get working example for click event
If I need to redirect, I typically use window.location.href
window.location.href=$(".warning a").attr('href');
when user click - 'booking' container, 'open_contents' have to show.
when user mouseout of 'open_contents' container, it have to hide or click outside 'open_contents' or anywhere on 'body' of the page.
with present below code, above point 1 works ok. But, i have increment/decrement button on 'open_contents' container. When user click button, 'open_contents' getting hide on every click. it should not get hide when i click button element on 'open_contents' container.
Please let me know your available comments.
HTML:
<div class="booking">
<div class="head">Ticket Booking</div>
</div>
<div id="open_contents" class="open_contents">
<div class="c_class">
<button type="button" class="btn btn-default minus" id="group-btn-minus" disabled="disabled" data-type="minus" data-field="quant[1]">
<span class="glyphicon glyphicon-minus"></span>
</button>
<input type="text" name="quant[1]" class="form-control group-input minus-plus-input" value="1" min="1" max="10">
<button type="button" class="btn btn-default plus" id="group-btn-plus" data-type="plus" data-field="quant[1]">
<span class="glyphicon glyphicon-plus"></span>
</button>
</div>
</div>
JS:
$(document).mouseup(function (e)
{
var $nonGroupBooking = $('.open_contents');
if (!$nonGroupBooking.is(e.target)
&& $nonGroupBooking.has(e.target).length === 0);
{
$nonGroupBooking.hide();
}
});
If you have bound the onclick event to $(document), all elements inside $(document) on click will also be considered as $(document).clicked
To stop the event bubbling up the event, you could use e.stopPropagation, read more on https://api.jquery.com/event.stoppropagation/
Here please try my example - https://jsfiddle.net/w07gnzgg/1/
I changed the $(".booking").on("click") behavior to show the .open_contents and also disabled $(".open_contents").on("mouseleave") event by default
Bind a click handler to the document to hide the contents, but also add a click handler to the contents that stops propagation (prevents the click event from reaching the document handler).
$(document).on('click', function(e) {
$('.open_contents').hide();
});
$('.open_contents').on('click', function(e) {
e.stopPropagation();
});