APM
APM

Reputation: 115

multiple dynamically created buttons with same name using ajax

I'm trying to implement a comment section and after button-press I want to update the comment section with ajax so the page doesn't have to refresh...

In this comment section I have 1 textarea + 1 button + a couple of hidden fields for every comment so users can answer specific comments...

so if there are 50 comments there are also 50 answer-fields, 1 for each...

And every thing works except for 1 thing...
- either I name all id's of the buttons and fields the same name (ie. id="sendAnswer" and id="answer", id="userID", ...) and then only the first one works...
-or I dynamically name them all (ie. id="sendAnswer(echo $i) ) thereby naming them all id="sendAnswer0", "sendAnswer1", "sendAnswer2", ... and then I do that for the textarea and hidden fields too (ie. id="answer(echo $i), id="userID(echo $i), ...)

And that works great too... except for now I have to make a jQuery-script for each... and since they are dynamically created that's difficult - as how many there are changes as more comments comes in...

Code for approach 1: naming them all the same...

$(document).ready(function(){
    "use strict";
    $("#sendAnswer").click(function(){
        var comment = $("#comment").val();
        var userID = $("#userID").val();
        var randomStringVideo = $("#randomStringVideo").val();
        var commentID = $("#commentID").val();
        $.ajax({
            type: "POST",
            url:'../scripts/comment.php',
            data:"comment="+comment+"&userID="+userID+"&randomStringVideo="+randomStringVideo+"&commentID="+commentID,
            success:function(){
                $("#commentDiv").load(location.href + " #commentDiv>*", "");
                $("#commentsDiv").load(location.href + " #commentsDiv>*", "");
                $("#comment").val('');
            }
        });
    });
});

And as I said... this works fine for the first one and the rest are duds...

Code for approach 2: I dynamically name all values...

$(document).ready(function(){
    "use strict";
    $("#sendAnswer"+$(this).val()).click(function(){ // this +$(this).val() doesn't work, only if I put #sendAnswer3 - then the 4th works and the rest are duds etc.
        var comment = $("#comment"+$(this).val()).val(); // works perfectly no matter what #sendAnswer I use
        var userID = $("#userID"+$(this).val()).val(); // works perfectly no matter what #sendAnswer I use
        var randomStringVideo = $("#randomStringVideo"+$(this).val()).val(); // works perfectly no matter what #sendAnswer I use
        var commentID = $("#commentID"+$(this).val()).val(); // works perfectly no matter what #sendAnswer I use
        $.ajax({
            type: "POST",
            url:'../scripts/comment.php',
            data:"comment="+comment+"&userID="+userID+"&randomStringVideo="+randomStringVideo+"&commentID="+commentID,
            success:function(){
                $("#commentDiv").load(location.href + " #commentDiv>*", "");
                $("#commentsDiv").load(location.href + " #commentsDiv>*", "");
                $("#comment"+$(this).val()).val(''); // this +$(this).val() doesn't work, only if I put #comment3 (matching the #sendAnswer)- then the 4th works and the rest are duds etc.
            }
        });
    });
});

With this I would have to name every single possible #sendAnswer-number + #comment-number for it to work... and with an infinite set of numbers to choose from 0-(infinite) - that's not viable...

If of any interest...
Php that dynamically creates the buttons and fields in question

.
.
.
<?php if ($_SESSION[numberOfComments] != 0) {
    for ($i=0; $i<$_SESSION[numberOfComments]; $i++) ?> // run through all comments that aren't answers to other comments
        // show comment info
        <div class="media">// answer comment box starts here
            <img class="mr-3 rounded" src="<?php $file = USER . $_SESSION['randomString'] . THUMBNAIL; if ( file_exists ( $file ) ) {echo $file; } else { echo USER . "default" . THUMBNAIL; } ?>" width="50" height="50" data-toggle="tooltip" data-placement="left" title="<?php echo $_SESSION['username']; ?>">
            <div class="media-body">
                <textarea class="form-control" rows="2" type="text" name="comment<?php echo $i; ?>" id="comment<?php echo $i; ?>" value="" placeholder="Great video!"></textarea>
                <input type="hidden" name="userID<?php echo $i; ?>" id="userID<?php echo $i; ?>" value="<?php if ( isset ( $_SESSION['id'] ) ) { echo $_SESSION['id']; } ?>">
                <input type="hidden" name="randomStringVideo<?php echo $i; ?>" id="randomStringVideo<?php echo $i; ?>" value="<?php echo $_GET['v']; ?>">
                <input type="hidden" name="commentID<?php echo $i; ?>" id="commentID<?php echo $i; ?>" value="<?php echo $_SESSION['commentID_getComment']; ?>">
                <button type="button" class="btn btn-primary float-right margin-top-5" id="sendComment<?php echo $i; ?>" value="<?php echo $i; ?>">
                    Answer
                </button>
            </div>
        </div> // answer comment box ends here
        <?php if ($_SESSION[numberOfAnswers][$i] != 0) {
            for ($j=0; $j<$_SESSION[numberOfAnswers][$i]; $j++) { ?> // run through all answer to this comment
                // show answer info
            <?php }
        }
    }
} ?>
.
.
.

Upvotes: 1

Views: 292

Answers (2)

Mohamed-Yousef
Mohamed-Yousef

Reputation: 24001

two ways .. 1st use classes instead of ids .OR. 2nd use selector id starts with [id^="something"] .. and on both ways you need to use $(this) to refer to the same section .. And for me its bad practice to use .load() to refresh the whole comment section you can directly get the specific comment and append it to the #commentDiv

by using $("#sendAnswer"+$(this).val()) $(this) in this case refer to nothing/window or something else but not to your element

$(document).ready(function(){
    "use strict";
    $('button[id^="sendAnswer"]').on('click',function(){
        var ThisIs = $(this);
        var ClosestDiv = ThisIs.closest('.media-body');
        var comment = ClosestDiv.find('textarea').val(); 
        var userID = ClosestDiv.find('[id^="userID"]').val(); 
        var randomStringVideo = ClosestDiv.find('[id^="randomStringVideo"]').val();
        var commentID = ClosestDiv.find('[id^="commentID"]').val();
        $.ajax({
            type: "POST",
            url:'../scripts/comment.php',
            data:"comment="+comment+"&userID="+userID+"&randomStringVideo="+randomStringVideo+"&commentID="+commentID,
            success:function(){
                var commentDiv = ThisIs.closest('.BigDiv').find('[id^="commentDiv"]'); // change `.BigDiv` with the div id/class which hold both commentDiv and comment section
                commentDiv.load(location.href + " #commentDiv>*", "");
                ClosestDiv.find('textarea').val('');
            }
        });
    });
});

Note: Change .BigDiv with the div id/class which hold both commentDiv and comment section

Upvotes: 2

Don&#39;t Panic
Don&#39;t Panic

Reputation: 14520

Using your current approach, where everything is identified with ids, the best option would be to use event delegation. Create just one event handler, on some parent element which exists at the time the page is loaded, which via delegation will handle any of your buttons - existing and future. Once the handler fires, you can determine which set of elements you are working with, and continue as normal.

Here's an example, using body as the parent, but you can use any element which contains all your present and future buttons/inputs, eg maybe you have a parent <div id="something">. This also assumes your button inputs are actual button elements, you'll have to adjust if that's not the case:

$(document).ready(function(){
    "use strict";

    // A single event handler on body, which handles any child button
    $("body").on('click', 'button', function(event) {

        // $(this) will be the button that was clicked.  We can find its id,
        // and if your buttons all have ids like sendAnswer1, sendAnswer2,
        // sendAnswerX, we can find the X
        var id = $(this).attr('id').replace('sendAnswer', '');

        // Now we can use the X to access each of this button's matching
        // inputs etc. Again this assumes your other elements have ids like 
        // commentX.
        var comment = $("#comment" + id).val(),
            userID = $("#userID" + id).val(),
            // ... etc, rest of your code

As well as the article linked above, the jQuery .on() docs have a good description of event delegation (see "Direct and delegated events").

Update

Another option, which I find neater, is to wrap each comment section in a div (or any identifying element, such that each comment/input/button set is nested inside it), and use only classes for each element. Again using event delegation, you can find the section which contains the button that was clicked, and therefore identify which input elements you're working with.

For example, your HTML might look like:

<div id="all-comments">
    <div class="comment-section">
        <textarea class="comment" name="whatever"> ... </textarea>
        <button name="button" type="submit">Post!</button>
        <!-- your hidden fields, etc -->
    </div>

    <div class="comment-section">
        ...
    </div>

    <!-- ... etc, many comment sections -->
</div>

Now your JS would look something like this:

$(document).ready(function(){
    "use strict";

    // A single event handler on the parent div, which handles any child button
    $("#all-comments").on('click', 'button', function(event) {

        // $(this) will be the button that was clicked.  We need to find the
        // .comment-section containing that button
        var div = $(this).closest('div.comment-section');

        // Now we can find all elements inside that particular section
        var comment = $(".comment", div).val(),
            userID = $(".userID", div).val(),
            // ... etc, rest of your code

Upvotes: 0

Related Questions