Link to home
Start Free TrialLog in
Avatar of evibesmusic
evibesmusicFlag for United States of America

asked on

How to use the .each function with timer in jquery?

Experts,

I'd like to:

1. Loop through a class of checkboxes using the .each() function
2. Determine if a given checkbox is checked
3. Then fire a custom function

I want the custom function to fire in 5 second intervals but, I can't figure out the syntax to get the setTimeout() function to work? Can anyone offer any assistance?

I ultimately want the send_email() function to fire once every 5 seconds so that I don't bombard the email server.

Cheers.

    function send_email(referral_id,email_address){
        $.ajax({
            method: 'POST',
            url: "send_email.php",
            data: {
                group_id: "<?php echo $_GET['group_id']; ?>",
                date: "<?php echo $_GET['date']; ?>",
                referral_id: referral_id,
                email: email_address
            }
            }).success(function(){
                //UPDATE THE CHECKBOX BELONGING TO INDICATE EMAIL SENT
            }).fail(function(){
                alert('Unable to save email. Email = '+ email_address);
        })//END AJAX
    }
    
    //SEND EMAIL FUNCTION - FIRES WHEN #send_email BUTTON IS CLICKED
    $("#send_email").click(function(){
         //LOOP THROUGH THE .email_member CHECKBOXES
        $(".email_member").each(function(){
            //IF THE CHECKBOX IS CHECKED FIRE THE send_email() FUNCTION
            if($(this).prop("checked")==true){
                var email_address = $(this).closest('tr').attr('email_address');
                var referral_id = $(this).closest('tr').attr('id');
               //
               //FIRE THIS FUNCTION EVERY FIVE SECONDS
               // 
               send_email(referral_id,email_address);
            }
        });
    });//END #send_email

Open in new window

Avatar of leakim971
leakim971
Flag of Guadeloupe image

for example :
function send_email(referral_id,email_address){
    $.ajax({
        method: 'POST',
        url: "send_email.php",
        data: {
            group_id: "<?php echo $_GET['group_id']; ?>",
            date: "<?php echo $_GET['date']; ?>",
            referral_id: referral_id,
            email: email_address
        }
    }).success(function(){
        //UPDATE THE CHECKBOX BELONGING TO INDICATE EMAIL SENT
    }).fail(function(){
        alert('Unable to save email. Email = '+ email_address);
    })//END AJAX
}

//SEND EMAIL FUNCTION - FIRES WHEN #send_email BUTTON IS CLICKED
$("#send_email").click(function(){
    //LOOP THROUGH THE .email_member CHECKBOXES
    $(".email_member").each(function(){
        //IF THE CHECKBOX IS CHECKED FIRE THE send_email() FUNCTION
        if($(this).is(":checked")){
            var email_address = $(this).closest('tr').attr('email_address');
            var referral_id = $(this).closest('tr').attr('id');
            //
            //FIRE THIS FUNCTION EVERY FIVE SECONDS
            //
            setInterval(function() {
                send_email(referral_id, email_address);
            }, 5*1000);
        }
    });
});//END #send_email

Open in new window

with updates :
<!DOCTYPE html>
<html>
<head>
</head>
<body>
<button id="send_email">SEND EMAIL</button>
<table>
    <tbody>
        <tr id="1" email_address="leakim@ee.com"><td><input class="email_member" type="checkbox"></td></tr>
        <tr id="2" email_address="evibesmusic@ee.com"><td><input class="email_member" type="checkbox"></td></tr>
    </tbody>
</table>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.min.js" integrity="sha512-894YE6QWD5I59HgZOGReFYm4dnWc1Qt5NtvYSaNcOP+u1T9qYdvdihz0PPSiiqn/+/3e7Jo4EaG7TubfWGUrMQ==" crossorigin="anonymous"></script>
<script>
    const group_id = "<?php echo $_GET['group_id']; ?>"; // store/get this using session variable instead
    const date = "<?php echo $_GET['date']; ?>"; // store/get this using session variable instead

    //SEND EMAIL FUNCTION - FIRES WHEN #send_email BUTTON IS CLICKED
    $("#send_email").on("click", ()=> {
        //LOOP THROUGH THE .email_member CHECKBOXES
        $(".email_member").each(function(){
            //IF THE CHECKBOX IS CHECKED FIRE THE send_email() FUNCTION
            if($(this).is(":checked")) {
                const row = $(this).closest('tr');
                const data = {
                    group_id: group_id, // useless if you use session variable
                    date: date, // useless if you use session variable
                    referral_id: row.attr('id'),
                    email: row.attr('email_address')
                };
                setInterval(() => {
                    $.post("send_email.php", data)
                        .done(function(){
                            //UPDATE THE CHECKBOX BELONGING TO INDICATE EMAIL SENT
                        }).fail(() => alert('Unable to save email. Email = '+ row.attr('email_address')));                    
                }, 5*1000); // FIRED EVERY 5s
            }
        });
    });//END #send_email
</script>
</body>
</html>

Open in new window


ASKER CERTIFIED SOLUTION
Avatar of Chris Stanyon
Chris Stanyon
Flag of United Kingdom of Great Britain and Northern Ireland image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
If you need to run this on the client, you can do this

https://jsfiddle.net/mplungjan/meg8bjyr/

I recommend some message to tell the user to wait

NOTE, I changed the <tr id="referralid" email_address="...@......" to data-id and data-email_address to conform with standard attributes


const data = {
  group_id: "<?php echo $_GET['group_id']; ?>", // this could be XSS injection sensitive
  date: "<?php echo $_GET['date']; ?>"
}
let emailArray;
let cnt = 0;

function send_email() {
  if (cnt >= emailArray.length) return; //stop
  const data = emailArray[cnt].data;
  $.ajax({
    method: 'POST',
    url: "send_email.php",
    data: data
  }).success(function() {
    emailArray[cnt].chk.checked = false;
    cnt++; // next
    setTimeout(send_email, 5000);
  }).fail(function() {
    alert('Unable to save email. Email = ' + emailArray[cnt].data.email_address);
  }) //END AJAX
}


//SEND EMAIL FUNCTION - FIRES WHEN #send_email BUTTON IS CLICKED
$("#send_email").click(function() {
  //LOOP THROUGH THE .email_member CHECKBOXES
  emailArray = $(".email_member:checked").map(function() {
    const $tr = $(this).closest('tr');
    const thisData = { ...data  } // clone
    thisData.email_address = $tr.data('email_address');
    thisData.referral_id = $tr.closest('tr').data('id');
    return {
      data: thisData,
      chk: this // the checkbox
    }
  }).get()
  console.log(emailArray)
  send_email();
}); //END #send_email

Open in new window

I am with Chris on this one. Collect the emails that need to be sent, send them to the server and do the delay sending there.

Also you might want to consider some sort of strategy to prevent your send script from being called by malicious users, using a nonce or similar, created by the server and validated when the send script is called.

Essentially what you are doing is putting business logic in your client code which is not a good practice.

Sidebar: you might want to reconsider this code
  group_id: "<?php echo $_GET['group_id']; ?>",
  date: "<?php echo $_GET['date']; ?>",

Open in new window

At the very least do a safe extraction of the $_GET before inserting into the code.What you have there is opening you up to a cross site scripting vulnerability.

$group_id = (int) ($_GET['group_id'] ?? 0;
$date = $_GET['date'] ?? false;
// validate $date and $group_id here before inserting into the script

Open in new window


from @Chris :
 it will just keep on sending emails !

I believe it was a requirement for some reasons... from my initial code posted, just replace :
$(".email_member").each(function(){

Open in new window

by :
$(".email_member").each(function(i){

Open in new window


and :
setInterval(() => {

Open in new window

by :
setTimeout(() => {

Open in new window


and finally :
}, 5*1000);

Open in new window

by :
}, i*5*1000);

Open in new window

<!DOCTYPE html><html><head></head><body>
<button id="send_email">SEND EMAIL</button>
<table>
    <tr id="1" email_address="leakim@ee.com"><td><input class="email_member" type="checkbox"></td></tr>
    <tr id="2" email_address="evibesmusic@ee.com"><td><input class="email_member" type="checkbox"></td></tr>
</table>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.min.js" integrity="sha512-894YE6QWD5I59HgZOGReFYm4dnWc1Qt5NtvYSaNcOP+u1T9qYdvdihz0PPSiiqn/+/3e7Jo4EaG7TubfWGUrMQ==" crossorigin="anonymous"></script>
<script>
    const group_id = "<?php echo $_GET['group_id']; ?>"; // store/get this using session variable instead
    const date = "<?php echo $_GET['date']; ?>"; // store/get this using session variable instead

    //SEND EMAIL FUNCTION - FIRES WHEN #send_email BUTTON IS CLICKED
    $("#send_email").on("click", ()=> {
        //LOOP THROUGH THE .email_member CHECKBOXES
        $(".email_member").each(function(i){
            if($(this).is(":checked")) { //IF THE CHECKBOX IS CHECKED FIRE THE send_email() FUNCTION
                const row = $(this).closest('tr');
                const data = { group_id: group_id, date: date, referral_id: row.attr('id'), email: row.attr('email_address') };
                setTimeout(() => {
                    $.post("send_email.php", data)
                        .done(function(){
                            //UPDATE THE CHECKBOX BELONGING TO INDICATE EMAIL SENT
                        }).fail(() => alert('Unable to save email. Email = '+ row.attr('email_address')));
                }, i*5*1000); // FIRE THIS FUNCTION at 0s for first email checked, 5s for second, 10s for third and so on
            }
        });
    }); //END #send_email
</script>
</body>
</html>

Open in new window


@leakim - sorry ... still flawed. The i you're passing in refers to the checkbox, regardless of whether it's ticked. If only the 10th checkbox is ticked, then it won't send an email for 50 seconds !! By then, the visitor could be long gone!
good point @Chris
updated in bold :
<!DOCTYPE html><html><head></head><body>
<button id="send_email">SEND EMAIL</button>
<table>
    <tr id="1" email_address="leakim@ee.com"><td><input class="email_member" type="checkbox"></td></tr>
    <tr id="2" email_address="evibesmusic@ee.com"><td><input class="email_member" type="checkbox"></td></tr>
</table>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.min.js" integrity="sha512-894YE6QWD5I59HgZOGReFYm4dnWc1Qt5NtvYSaNcOP+u1T9qYdvdihz0PPSiiqn/+/3e7Jo4EaG7TubfWGUrMQ==" crossorigin="anonymous"></script>
<script>
    const group_id = "<?php echo $_GET['group_id']; ?>"; // store/get this using session variable instead
    const date = "<?php echo $_GET['date']; ?>"; // store/get this using session variable instead

    //SEND EMAIL FUNCTION - FIRES WHEN #send_email BUTTON IS CLICKED
    $("#send_email").on("click", ()=> {
        //LOOP THROUGH THE .email_member CHECKBOXES
        let i = 0;
        $(".email_member").each(function(){
            if($(this).is(":checked")) { //IF THE CHECKBOX IS CHECKED FIRE THE send_email() FUNCTION
                i++;
                const row = $(this).closest('tr');
                const data = { group_id: group_id, date: date, referral_id: row.attr('id'), email: row.attr('email_address') };
                setTimeout(() => {
                    $.post("send_email.php", data)
                        .done(function(){
                            //UPDATE THE CHECKBOX BELONGING TO INDICATE EMAIL SENT
                        }).fail(() => alert('Unable to save email. Email = '+ row.attr('email_address')));
                }, i*5*1000); // FIRE THIS FUNCTION at 0s for first email checked, 5s for second, 10s for third and so on
            }
        });
    }); //END #send_email
</script>
</body>
</html>

Open in new window

Avatar of evibesmusic

ASKER

@All:

Thanks for your feedback and for pouring over my code to suggests helpful edits.

As suggested, I've altered my approach and am handling the timing of the sending of emails server-side via PHP. Simply by adding the sleep() function to my code handled the staggered approach that I was looking for.

Appreciate the feedback Experts!!!

Cheers,

Brian B.
Hey Brian,

Glad you're going the server-side route. It will give you a more robust solution in the long run. I would suggest if you want a really solid solution, look into queuing your email tasks at the server, rather than just calling sleep(). The sleep method might work for a couple of iterations, but it's effectively 'blocking' code, so your script will run for as long as it needs to send out the emails (60 seconds for 12 emails). This is likely to leave the user hanging, thinking the page has locked up.

A very simple approach to a queue system is that your front page submits it's data to a server-side script. That script just saves the data into a database and then finishes, returning control back to the user (milliseconds). You then have a queue manager / processor. This is the script that is actually responsible for handling the heavy lifting (sending out emails in your case). You set this to run on a cronjob (a server-side scheduled task), say every minute. That script will check the database and process the records it finds as and when required. This will be the most responsive, robust and scalable approach to your requirements.

Food for thought maybe :)

Good luck with your project
out of the main topic and question, I don't see why we should manage email delivery at a programming level
it's a mail server task and any of them (sendmail, MS exchange, IBM domino, ...) can manage a huge amount of emails.
Email server have their own queue "process"
I'm supposing we've not a million of email to send here as your UI require to check a box :))
The only thing coming in mind is you would like to prevent your email to be marked as spam but this is not doable at this level.