I have UI with a list of users in a table. Each entry / user has two buttons. One of the buttons is a 'check' button.
<button type="submit" id="$id_from_db" class="bg-aqua btnCheckUser">Check</button>
When a check button is clicked for a particular user a POST request is sent to a server. While the request is loading all buttons should be disabled, the button which started the request should change its class from bg-puple to bg-aqua
and its text from Check to Checking.... If the request returns an error (which is indicated by result having the value 1) the button should change its class from bg-aqua to bg-danger. If there is no error the class should change from bg-aqua to bg-navy and the text from Checking... to User Okay.
I'm able to achieve this but it does not only affect the button which started the request but all buttons.
This is my code:
$(document).on("click", ".btnCheckUser", function() {
var Item_Number = $(this).attr("Item_Number");
/************************** MANIPULATE BUTTONS *******************************************************************************/
$(this).removeClass('bg-purple').addClass('bg-aqua');
$(this).html('Checking ...');
$(this).attr("disabled", true);
$('.btnViewUser').attr("disabled", true);
/******************************* PROCESS AJAX **************************************************************************/
var value = {
Item_Number: Item_Number
};
$.ajax({
url: "./plugins/serverside/CheckUserStatus",
type: "POST",
data: value,
success: function(data, textStatus, jqXHR) {
var data = jQuery.parseJSON(data);
if (data.result == 1) {
$(this).removeClass('bg-aqua').addClass('bg-danger');
$("#CheckUser").html('Failed!');
$('.btnCheckUser').attr("disabled", false);
$('.btnViewUser').attr("disabled", false);
setTimeout(function() {
var table = $('#User_Table').DataTable();
table.ajax.reload(null, false);
}, 3500);
} else if (data.result == 2) {
//------------------------ IF User Okay -------------------------------------------------------------------------------------------------------------------
$("#CheckUser").removeClass('bg-aqua').addClass('bg-navy');
$("#CheckUser").html('User Okay');
$('.btnCheckUser').attr("disabled", false);
$('.btnViewUser').attr("disabled", false);
}
How can I fix this and only affect the button which started the request, but still disable all buttons when a request is loading?
JSFiddle
I stripped the code down to the parts which matter for the button logic. I'm using a dummy API and I randomly generate the result value as I have no access to the actual API you're using.
$(() => $('.btnCheckUser').on('click', ({ target }) => clickHandler(target)));
function clickHandler(button) {
// Disable all buttons when one of them is clicked
$('.btnCheckUser').prop('disabled', true);
$('.btnViewUser').prop('disabled', true);
// Remove all non standard classes, then add 'loading' class and text
$(button).removeClass('bg-purple').removeClass('bg-navy').removeClass('bg-danger').addClass('bg-aqua');
$(button).text('Loading...')
$.ajax({
url: 'https://reqres.in/api/users?delay=3',
success: raw => {
// Random result value
const result = Math.random() > 0.5 ? 1 : 2;
if (result === 1) {
// Something went wrong, add 'danger' class and text
$(button).addClass('bg-danger');
$(button).text('Failed!')
} else if (result === 2) {
// Everything went fine, add 'success' class and text
$(button).addClass('bg-navy');
$(button).text('Success!');
}
},
error: () => {
// Add 'danger' class if something goes wrong
$(button).addClass('btn-danger');
},
complete: () => {
// Enable all buttons as we got a response
$('.btnCheckUser').prop('disabled', false);
$('.btnViewUser').prop('disabled', false);
// Remove 'loading' class as we got a response
$(button).removeClass('bg-aqua');
}
});
}
This should give you the expected behavior.
hello there this is truly easy what you have to do is
function TheClickerFunction(){
$("body").on("click",".YourButton",function(){
//this will only affect the clicked element
$(this).attr("disabled",true);
});
}
$(document).ready(function(){
TheClickerFunction();
});
/*
i think creating a global counter and increment it on each element would b better*/
var GlobalCounter= 0;
function DynamicBuilder(){
var stringBuilder="";
for(var i = 0; i< lengthOfYourElement ; i++){
GlobalCounter+=1;
stringBuilder+="<input type='submit' id='myButton"+GlobalCounter+"'></input>";
}
$("body").append(stringBuidler);
}
in this way each time a click is made
$(".yourClass").click(function(){
var elementID = $(this).attr("id");
//here you can do what ever you want with its id
})
*/
Related
I have two scripts handling my multistep form. The first handles the visibility of each step (input/selector boxes) within the form along with the visibility of the pagination buttons (prev, next).
This looks like:
const previousButton = document.querySelector('#prev')
const nextButton = document.querySelector('#next')
const submitButton = document.querySelector('#submit')
const tabTargets = document.querySelectorAll('.tab')
const tabPanels = document.querySelectorAll('.tabpanel')
const isEmpty = (str) => !str.trim().length
let currentStep = 0
// Validate first input on load
validateEntry()
// Next: Change UI relative to current step and account for button permissions
nextButton.addEventListener('click', (event) => {
event.preventDefault()
// Hide current tab
tabPanels[currentStep].classList.add('hidden')
tabTargets[currentStep].classList.remove('active')
// Show next tab
tabPanels[currentStep + 1].classList.remove('hidden')
tabTargets[currentStep + 1].classList.add('active')
currentStep += 1
validateEntry()
updateStatusDisplay()
})
// Previous: Change UI relative to current step and account for button permissions
previousButton.addEventListener('click', (event) => {
event.preventDefault()
// Hide current tab
tabPanels[currentStep].classList.add('hidden')
tabTargets[currentStep].classList.remove('active')
// Show previous tab
tabPanels[currentStep - 1].classList.remove('hidden')
tabTargets[currentStep - 1].classList.add('active')
currentStep -= 1
nextButton.removeAttribute('disabled')
updateStatusDisplay()
})
function updateStatusDisplay() {
// If on the last step, hide the next button and show submit
if (currentStep === tabTargets.length - 1) {
nextButton.classList.add('hidden')
previousButton.classList.remove('hidden')
submitButton.classList.remove('hidden')
validateEntry()
// If it's the first step hide the previous button
}
else if (currentStep == 0) {
nextButton.classList.remove('hidden')
previousButton.classList.add('hidden')
submitButton.classList.add('hidden')
// In all other instances display both buttons
}
else {
nextButton.classList.remove('hidden')
previousButton.classList.remove('hidden')
submitButton.classList.add('hidden')
}
}
function validateEntry() {
let input = tabPanels[currentStep].querySelector('.form-input')
// Start but disabling continue button
nextButton.setAttribute('disabled', true)
submitButton.setAttribute('disabled', true)
// Validate on initial function fire
setButtonPermissions(input)
// Validate on input
input.addEventListener('input', () => setButtonPermissions(input))
// Validate if bluring from input
input.addEventListener('blur', () => setButtonPermissions(input))
}
function setButtonPermissions(input) {
if (isEmpty(input.value)) {
nextButton.setAttribute('disabled', true)
submitButton.setAttribute('disabled', true)
}
else {
nextButton.removeAttribute('disabled')
submitButton.removeAttribute('disabled')
}
}
For the first input box, i have a Jquery autocomplete which pulls data from a json file and presents it as a select option.
$(document).ready(function() {
$.ajax({
dataType: "json",
type: 'Get',
contentType: 'application/json; charset=utf-8',
xhrFields: {
withCredentials: true
},
crossDomain: true,
cache: true,
url: "AvailableFeatures.json",
success: function(data) {
var array = $.map(data.Table, function(item) {
return {
label: item.label,
value: item.label
}
});
$("#txtfeature").autocomplete({
source: array,
minLength: 3,
open: function() {},
close: function() {},
focus: function(event, ui) {},
select: function(event, ui) {},
change: function(event, ui) {
if (!ui.item) {
$("#txtfeature").val("");
}
}
});
},
error: function(data) {}
});
});
I had been using the change event to limit the content of the input box to an item selected and if a user clicks elsewhere and the data isn't as per the selected option, it empties the content of the input box.
The problem I have is that if a user types random text in the box and simply hits continue on the form, it blanks the field but continues on to the next input box.
How can i prevent this from occurring?
Should i even have two scripts handling this form/autocomplete?
I'm sure there's a more efficient way of doing what i'm trying to achieve but i'm a novice in this space and trying to learn whilst trying to build upon an idea.
Any help would be much appreciated!
How can I let my form alert re-start once the user re-click the button if the input still empty
What I made :
after the click it will check if the inputs empty will stop the code and show alert. but once the alert appeared if I re-click the button again its not work again!
$(document).ready(function(){
function requierd()
{
$('#allrequierd').addClass("animated");
$('#allrequierd').addClass("shake");
$('#alertDanger').removeClass("hide");
setTimeout( "$('#alertDanger').fadeOut();", 4000);
}
function pass()
{
$('#alertDanger').addClass("hide");
$('#alertsuccess').removeClass ("hide");
$('#visitorFullName').val('');
$('#visitorPhoneNumber').val('');
$('#visitorEmail').val('');
$('#visitorMsg').val('');
$('#alertsuccess').addClass ("animated");
$('#alertsuccess').addClass ("bounceIn");
setTimeout( "$('#alertsuccess').fadeOut();", 4000);
}
$('#sendContactMsg').click(function ()
{
var visitorFullName = $('#visitorFullName').val();
var visitorPhoneNumber = $('#visitorPhoneNumber').val();
var visitorEmail = $('#visitorEmail').val();
var visitorMsg = $('#visitorMsg').val();
var visitorCallMethod = $('#visitorCallMethod').val();
var dataString = 'visitorFullName='+visitorFullName+'&visitorPhoneNumber='+visitorPhoneNumber+'&visitorEmail='+visitorEmail+'&visitorMsg='+visitorMsg+'&visitorCallMethod='+visitorCallMethod;
if(visitorFullName==''||visitorPhoneNumber==''||visitorEmail==''||visitorMsg=='')
{
requierd();
}else{
// AJAX Code To Submit Form.
$.ajax({
type: "POST",
url: "functions/sContactus.php",
data: dataString,
cache: false,
success: function(result){
// alert(result);
if (result == "Success") {
// alert("DONE");
pass();
}else {
alert("Sorry Try Again")
}
}
});
}
return (start);
});
// END jquery
});
When you fire the alert the first time, your #alertDanger is getting the .fadeOut() which will apply inline display:none style to that element. You're not removing that inline style at any point, and in fact you're also not adding back the "hide" class either, so your #alertDanger will remain hidden.
What you should do is "reset" the alert div after you fade it out:
function removeAlert() {
$('#alertDanger').fadeOut();
$('#alertDanger').addClass("hide").css("display","block");
}
And in your required() function:
setTimeout( removeAlert, 4000);
Is there a method, similar to $(document).ready(), that can be applied
to an arbitrary element? For example, if an ajax call sets the content
of a DIV and includes a lot of IMG tags, is there a way to trigger
a function call when all of the images have completed loading? Something
along the lines of:
$.ajax({
url: '/get/my/page.php',
dataType: "html",
success: function(response)
{
$('#my_element').html(response);
$('#my_element').ready(function() {alert('all images loaded');});
}
});
Thanks for your advice.
If you're specifically interested in images being loaded, then you can try imagesLoaded, which seems to cover the example case that you mention.
Identical to $("document").ready() -- no, however, there are ways that you can make it work:
Put whatever function you need executed as a callback to the AJAX call:
$.get("link.php?item=1", function() {
//callback function here
})
You can have an onchange event listener that triggers whenever the div is being changed and you can specifically trigger a function after a certain number of elements is loaded:
$("#div").on('change', function() {
if ( $("#div").length > DESIRED_NUMBER_OF_ELEMENTS + 1)
{
//Execute function here
}
})
The on('change') can be used in any which way you want: it can trigger a function based on the number of elements, based on the nature of elements inside (if it's an image, a link, another div, etc), anything you can think of.
Something along these lines should do what you want.
onImagesLoaded('#my_element img',function(){
console.log('images have all loaded')
});
function onImagesLoaded(seletor,callback){
var $images = $(seletor);
var count = $images.length;
$images.each(function(img){
//create an image
var tempImage = new Image();
//set a function for the onload event
tempImage.onload = function(){
count--;
//if the count is 0 it means all images are done loading
if(count===0){
callback();
}
};
//set the source to the src of the image.
tempImage.src = $(img).attr('src');
});
}
Try
var request = $.ajax({url: "/get/my/page.php", dataType: "html"});
request.done(function (data, textStatus, jqxhr) {
// parse returned `data` string for html
var html = $.parseHTML(data),
//
len = html.length,
div = $("div");
// do stuff when all images loaded into `div`
div.on("imgsready", function(e, htmlimgs, divimgs) {
console.log("ready", htmlimgs, divimgs);
$(e.target).prepend(divimgs + " images ready<br>" )
});
// set `div` html contents to `html` string
$.when(div.html(html)
// return image `on` `load` event
, $("img", div)
.load(function(e) {
// return image `promise` object
return $(e.target).promise()
})
)
.then(function (el, i) {
// if `div` contains `len` length images , call `imgsready` event
return el.children(i).length === (i.length && len)
&& el.children(i).eq(len -1).is("*")
? el.trigger("imgsready", [len, i.length])
// else check again in 1000ms
: setTimeout(function() {
el.children(i).eq(len -1).is("*") ?
el.trigger("imgsready", [len, i.length])
: console.log(el.children(i).length)
},1000)
});
});
jsfiddle http://jsfiddle.net/guest271314/vhuaen71/
So when someone hits Reply, I am attempting to pop-up a form to type your response. Once the form is submitted, it disappears until the next time you hit Reply.
This is working except after the 1st time, I am submitting the information twice. If I do it a third time, the form submits three times. Essentially what is happening is the previous form doesn't seem to be resetting after I hide it again.
I checked this website/google and have tried using reset() but it didn't work. Below is the code:
$(document).on('click', '.secretfeed button', function () {
var message_id = $(this).attr('name');
$(".comment_box").show();
$("#m_id").val(message_id);
var value = document.getElementById("m_id").value;
$('#comment_form').submit(function (e) {
e.preventDefault();
var commentData = $(this).serialize();
$.post('../../process_comment.php', commentData, processData);
function processData(data) {
//$('comment_form').reset()
$(".comment_box").hide();
$('#comment_form')[0].reset();
RefreshFeed();
}
});
});
Rather than initializing the submit function on every click, move it outside the click function. jQuery may be creating an instance of it for each click.
$('#comment_form').submit(function (e) {
e.preventDefault();
var commentData = $(this).serialize();
$.post('../../process_comment.php', commentData, processData);
function processData(data) {
//$('comment_form').reset()
$(".comment_box").hide();
$('#comment_form')[0].reset();
RefreshFeed();
}
});
$(document).on('click', '.secretfeed button', function () {
var message_id = $(this).attr('name');
$(".comment_box").show();
$("#m_id").val(message_id);
var value = $("#m_id").val();
});
The alternative is to unbind the click function before reusing it.
We want a reusable way to handle the state. We will save the state of the button in a boolean which gets turned on and off depending on the status of the request. The pattern is the following:
var isSending = false;
function onSubmit() {
isSending = true;
// Send data
}
function onComplete() {
// done sending data
isSending = false;
}
if (!isSending) {
onSubmit();
}
// When data sending is finished:
onComplete();
The above can be encapsulated in a more functional way that uses promises to manage the state. (jQuery AJAX functions all return a promise-like object):
function oneAtATimeFunction(promisedFunction) {
var pendingPromise;
function reset() { pendingPromise = null; }
return function() {
if (pendingPromise) { return pendingPromise; }
pendingPromise = promisedFunction.apply(promisedFunction, arguments)
.always(reset);
return pendingPromise;
}
}
function submitForm() {
return $.ajax({
url: '/foo',
method: 'POST',
data: { data: 'from form' }
});
}
$('#submit-button').on('click', oneAtATimeFunction(submitForm));
Adding a little flare to the UI We can add a way to turn on and off the submit button. First we will define a helper function to handle the on and off state:
function buttonEnable(enabled) {
$('#submit-button').attr('disabled', !enabled);
}
buttonEnable(false); // disable the button
buttonEnable(true); // enable the button
Putting it all together:
function onClick() {
buttonEnable(false);
return onSubmit()
.always($.proxy(buttonEnable, null, true));
// The above is also the same as:
// .always(function() { buttonEnable(true); });
}
$('#submit-button').on('click', oneAtATimeFunction(onClick));
To see this in action here is a JSBin example.
Using tutorials found i'm currently loading new pages with this:
$("a.nav-link").click(function (e) {
// cancel the default behaviour
e.preventDefault();
// get the address of the link
var href = $(this).attr('href');
// getting the desired element for working with it later
var $wrap = $('#userright');
$wrap
// removing old data
.html('')
// slide it up
.hide()
// load the remote page
.load(href + ' #userright', function () {
// now slide it down
$wrap.fadeIn();
});
});
This loads the selected pages perfectly, however the pages have forms that themselves use ajax to send the following:
var frm = $('#profileform');
frm.submit(function (ev) {
$.ajax({
type: frm.attr('method'),
url: frm.attr('action'),
data: frm.serialize(),
success: function (data) {
alert(data)
}
});
However this is not sending the form as it did before the page itself was called to the parent page via ajax. Am I missing something? Can you not use an ajax call in a page already called by ajax?
I also have other issues, for example I disable the submit button unless there are any changes to the form, using:
var button = $('#profile-submit');
var orig = [];
$.fn.getType = function () {
return this[0].tagName == "INPUT" ? $(this[0]).attr("type").toLowerCase() : this[0].tagName.toLowerCase();
}
$("#profileform :input").each(function () {
var type = $(this).getType();
var tmp = {
'type': type,
'value': $(this).val()
};
if (type == 'radio') {
tmp.checked = $(this).is(':checked');
}
orig[$(this).attr('id')] = tmp;
});
$('#profileform').bind('change keyup', function () {
var disable = true;
$("#profileform :input").each(function () {
var type = $(this).getType();
var id = $(this).attr('id');
if (type == 'text' || type == 'select') {
disable = (orig[id].value == $(this).val());
} else if (type == 'radio') {
disable = (orig[id].checked == $(this).is(':checked'));
}
if (!disable) {
return false; // break out of loop
}
});
button.prop('disabled', disable);});
However this also doesn't work when pulled to the parent page. Any help much appreciated! I'm really new to ajax so please point out any obvious mistakes! Many thanks in advance.
UPDATE
Just an update to what i've found. I've got one form working by using:
$(document).on('mouseenter', '#profile', function() {
However the following:
$(document).on('mouseenter', '#cancelimage', function() {
$('#cancelimage').onclick=function() {
function closePreview() {
ias.cancelSelection();
ias.update();
popup('popUpDiv');
$('#imgForm')[0].reset();
} }; });
Is not working. I understand now that I need to make it realise code was there, so I wrapped all of my code in a mouseover for the new div, but certain parts still don't work, so I gave a mouseover to the cancel button on my image form, but when clicked it doesn't do any of the things it's supposed to.
For anyone else who comes across it, if you've got a function name assigned to it, it should pass fine regardless. I was trying to update it, and there was no need. Doh!
function closePreview() {
ias.cancelSelection();
ias.update();
popup('popUpDiv');
$('#imgForm')[0].reset();
};
Works just fine.