I am running queries in Wordpress that should return a customer's three most recent orders. When a user clicks on the "Change" button, it should open an order form with a "Save" and "Cancel" button.
My jQuery is only working on the first result of the query. When I click the "Change" button on the second and third entries, nothing happens.
I've tried doing a loop on the jQuery, but it still only applies to the first result, so I am thinking I need to attach a variable unique ID to "order_form" or request the jQuery to run three times.
Suggestions on what I should try?
function hgf_myhome_dash() {
foreach( array_combine( $menuid, $custom_field_data ) as $menuid => $custom_field_data ) {
$user_menu = json_decode( stripcslashes( $custom_field_data ) );
// The button
echo '<div id="change_button" style="float: left;">' . do_shortcode('[button]Change this menu[/button]') . '</div>';
// The loop
foreach( $user_menu as $single ){
if( $single->id ) {
echo get_post( $single->id )->post_title;
}}
// The form
echo '
<div id="form_show" class="modal">
<form id="order_form" action="" method="POST">
<div class="modal-content">
<div class="col_title"><h3>'.__('Current Menu' ,'rm_domain').'</h3>
<input type="button" id="cancel_button" class="cancel_menu" value="Cancel" />
<input type="submit" class="save_menu" style="" name="msubmit" id="msubmit" value="Save" />
</div>
I am text, hear me roar.
</div>
</form>
</div>
';
}
}
And here is my jQuery:
jQuery(document).ready(function($){
$("#order_form").hide();
$( function() {
$( "#change_button" ).on( "click", function() {
$("#form_show").css('display', 'block');
$("#order_form").show();
});
$( "#cancel_button" ).on( "click", function() {
$("#form_show").css('display', 'none');
$("#order_form").hide();
});
} );
});
The id attributes must be unique. You can’t have more than one tag with the same ID. When you have several elements with the same ID, the browser will ignore all the IDs except the first one.
So, only the first appearances of id="change_button", id="form_show", id="order_form", id="cancel_button" will be counted. Other tags will behave as if they don’t have IDs.
Instead of ID, you can use the class element, which doesn’t need to be unique, like this:
echo '<div class="change_button" style="float: left;">' . do_shortcode('[button]Change this menu[/button]') . '</div>';
Then, you need to change your selectors: the #change_button searches for the element with id="change_button", and .change_button searches for elements with class="change_button" (you can have several classes separated by spaces, like class="some_class change_button some_other_class", and .change_button will still work).
However, there is a rub: the selector will find all the elements with this class. So, $(".form_show").css('display', 'block'); will show all the forms with class="form_show", and clicking on any button will show all the forms. Which is not what you need.
So, your code needs to know which form is clicked, and select the correct one.
You can access the button you’ve clicked with this inside the click handler. Note that this would be a DOM element and not a jQuery object. To convert it into a jQuery object, you need to use $(this).
You can use jQuery functions on $(this) to get elements around the button. For example, the .next() function will return a jQuery object containing the next element. So, you could use $(this).next().show() to show the element after .change_button (which is the form).
However, I would advise agains using .next. It makes the JavaScript too dependent on the layout. The designer might need to add some other tags between the button and the form, and then you’d need to change your code.
Instead, you can add some data into the .change_button that will allow to identify the form that needs to be opened. For example, you can assign numbers to all your forms, and add that number to the .change_button.
To add custom data to any element, you can use data-something="any data". This is HTML5 tag for adding arbitrary data. It is very handy because it can be accessed by using $(this).data('something') in jQuery.
So, you can add unique index to your buttons, and add that index to your form. You can add unique index like this:
function hgf_myhome_dash() {
$index = 1;
foreach( array_combine( $menuid, $custom_field_data ) as $menuid => $custom_field_data ) {
$user_menu = json_decode( stripcslashes( $custom_field_data ) );
// The button
echo '<div class="change_button" data-index="' . $index . '" style="float: left;">' . do_shortcode('[button]Change this menu[/button]') . '</div>';
// The loop
foreach( $user_menu as $single ){
if( $single->id ) {
echo get_post( $single->id )->post_title;
}
}
// The form
echo '
<div id="form_show_' . $index . '" class="modal">
<form id="order_form_' . $index . '" action="" method="POST">
<div class="modal-content">
<div class="col_title"><h3>'.__('Current Menu' ,'rm_domain').'</h3>
<input type="button" class="cancel_menu cancel_button" value="Cancel" />
<input type="submit" class="save_menu" style="" name="msubmit" id="msubmit_' . $index . '" value="Save" />
</div>
I am text, hear me roar.
</div>
</form>
</div>
';
}
++$index;
}
Note that I’ve used class for change_button and cancel_button (because there might be several buttons with these classes), but I’ve kept id for form_show_{INDEX}, order_form_{INDEX}. This is because, when we add index, the IDs become unique (there will be only one form_show_1, only one form_show_2 and so on in the document), so we use id and not class.
N.B. IDs must be unique, so you can only call your function hgf_myhome_dash only once per page. If you call it more than once, your JavaScript will only work correctly for the first instance. If you want to call the function many times, you can use some global variable instead of $index — but then you probably need to rename it to something longer, to avoid name clashes.
Then, you can change your JS code like this:
jQuery(document).ready(function($){
$("#order_form").hide();
$( function() {
$( ".change_button" ).on( "click", function() {
var index = $(this).data('index');
$("#form_show_" + index).css('display', 'block');
$("#order_form_" + index).show();
});
$( "#cancel_button" ).on( "click", function() {
var index = $(this).data('index');
$("#form_show_" + index).css('display', 'none');
$("#order_form_" + index).hide();
});
} );
});
P.S. By the way, $menuid and $custom_field_data are not defined inside your function. Since it’s not a matter of your question, I assume you’ve removed some part of the code code. If not, you probably need to make them global, or to pass them via function arguments.
Related
I'm using jQuery UI Sortable to let the user sort on a queried data, and saving the sorted data into another table. I'm putting the changed order into a hidden field and saving the data.
HTML
<?php
// $queried_data is queried in LIFO order from a table, suppose an order like: 1,2,3,4
$saved_data = '2,3,4,1'; //queried as a string from another table
?>
<div class="data-sorter">
<ul id="sortable-data">
<?php foreach($queried_data as $key => $value) : ?>
<li id="<?php echo $value->ID; ?>">
<?php echo $value->title; ?>
</li>
<?php endforeach; ?>
</ul>
<!-- HIDDEN FIELD: putting saved data here, and updating according to the changed data, and finally saving as a comma-separated string -->
<input type="hidden" class="data-sorted" value="<?php echo $saved_data; ?>">
</div>
JAVASCRIPTS
jQuery( document ).ready(function($) {
"use strict";
var sortable_data = $( "#sortable-data" );
sortable_data.sortable({
update: function( event, ui ) {
var list_id = this.id,
item_moved = ui.item.attr('id'),
order = $(this).sortable('toArray'),
positions = order.join(',');
var hidden_field = $('#'+ list_id).parent().find('.data-sorted');
hidden_field.val(positions).trigger('change');
}
});
sortable_data.disableSelection();
});
But as you can see the $queried_data ≠ $saved_data. So, after refresh, the PHP loop is putting things into original order. I'm okay with that. I just need to manipulate the DOM using the jQuery UI sortable so that they appear according to the order, got from the $saved_data.
I need a way to trigger jQuery UI sortable (I did in line#6 in Javascripts) as well as set the value according to the $saved_data (that available as a value of the hidden field .data-sorted).
I understand that I need the Sortatble create event, but I can't figure out, how.
Check this logic, should work. Considered that li id is the same as comma separated values.
On page load call function reorder() after sortable call.
$("#sortable-data").sortable({update: reorder});
function reorder()
{
orderArray = $('.data-sorted').val();
elementContainer = $("#sortable-data")
$.each(orderArray, function(key, val){
elementContainer.append($("li#"+val));
});
}
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 }
}
}
} ?>
.
.
.
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
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
I have a query that gets the latest 10 rows in a table and loops 10 times to echo HTML that includes some of the information taken from the table, to something similar like below (pseudo code):
query = <GET 10 LATEST ROWS FROM TABLE>
$name = <ONE VALUE FROM TABLE>;
$name2 = <ANOTHER VALUE FROM TABLE>;
echo '<div class="style1">' . $name . '</div> <img src="image.png" /> <div class="style2">' . $name2 . '</div>';
What I'm having trouble with is that, if a user clicks the image, I need to run some Ajax to show another piece of HTML based on the variable $name.
The problem is that, since I'm echoing 10 rows from the table, how can I get the value of that one variable when the image is clicked?
Please help!
give each div an id based on the value of $name. and you use $name for your ajax call to get to next step.
Wrap the grouping you need.
PHP:
<div class="style-container">
<div class="style1"><?=$name;?></div>
<img src="image.png">
<div class="style2"><?$=name2;?></div>
</div>
Then you can use JS to loop through by container and get the name, no matter what the values inside may be, with or without quotes and other special characters.
JS:
$('.style-container').each( function( i, el ) {
var $el = $(el),
name = $el.find( '.style1' ).text();
$el.find( 'img' ).on( 'click', function() {
$.ajax({
url : 'whatever.php',
data : { name : name }
});
});
});
Note I would only do this if using the markup is your only option. You may want to echo out a json_encode of data inside a JS tag. Then you can iterate and use a templating engine like mustache to print out the markup. Then you could use AJAX to open a URL based on the data rather than the markup.
i am using while loop to output data in
<p><?php echo $title;?></p></font></a><button id=show>show()</button> <button id=hide>hide()</button>
my show hide function is
$("#show").click(function () {
$("p").show('fast');
});
$("#hide").click(function () {
$("p").hide('fast');
});
$("#reset").click(function(){
location.reload();
});
now when i am clicking show hide only the first show hide loop is working and it shows/hides all the data not just the one i clicked
Change the code to use this, like so:
$(this).prev('p').show('fast');
You will need to do this in each JQuery .click section.
Edit:
Another good point which has been mentioned, you are using an ID for your element which won't allow this to work on more than one. Your new markup should look like:
<p><?php echo $title;?></p></font></a><button class="show">show()</button>
and the JQuery:
$(".show").click(function () {
$(this).prev('p').show('fast');
});
Welcome to SO. Nice to see that you have formated your first question nicely.
Few things you might want to change.
As you are going through a loop, make sure you use a counter inside the loop and add the counter to the id. This will make the id a unique identifier. Also wrap them inside a div.
$counter = 0;
forloop {
$counter++;
<div>
<p><?php echo $title;?></p></font></a><button id="show<?php echo $counter; ?>">show()</button>
</div>
}
So now your id will be unique.
Now you can write your jquery in the below way.
$("button").click(function () {
$(this).attr('id'); //not required but incase you need the id of the button which was clicked.
$(this).parent().find("p").show('fast');
});
$("button").click(function () {
$(this).parent().find("p").hide('fast');
});
Two things: 1. I think you can only have one element with one id, such as #show. If you want to reference more buttons, you should use a class, such as this: show() (as I understand the buttons are output in a loop, there will be more of them).
Second: inside your javascript code, you do $("p")... - this references all elements on the page. I think you should use $(this) and start from there, check out this article, it explains a lot: http://remysharp.com/2007/04/12/jquerys-this-demystified/
There are many ways to go at this. Here's one:
First, add the loop number to the IDs (let's say it's $i)
<p id="TITLE_<?php echo $i; ?>" style="display:none;"><?php echo $title;?></p>
<input type="button" class="show" data-number="<?php echo $i; ?>"/>
<input type="button" class="hide" data-number="<?php echo $i; ?>" />
Your functions will then be:
$(".show").click(function () {
$("TITLE_" + $(this).data('number')).show('fast');
});
$(".hide").click(function () {
$("TITLE_" + $(this).data('number')).hide('fast');
});
Of course there are ways to do it via JQUERY without the use of the $i.
Edit: To have the tags hidden on page load, either use the style=display:none as I have added in the tag above, or you can use JQuery as follows:
$(document).ready( function() {
$("p[id^=TITLE_]").hide();
// this will retrieve all <p> tags with ID that starts
// with TITLE_ and hide them
});
EDIT: THE PROBLEMS WERE CAUSED BY AN IMPROPERLY LOADING JQUERY LIBRARY, AN ISSUE NOT SPELLED OUT CLEARLY IN MY ORIGINAL QUESTION. APOLOGIES.
I'm using jquery to hide, post, then echo the results of a php query to two separate divs. I then want to be able to swap the resulting images across these divs. The first part works fine, but I'm unable to get any other jquery scripts to work on these divs (e.g. sortable, droppable, etc).
I'm new to scripting within the past few weeks. I think I need to json encode the php stuff before I send it, but I'm having trouble finding clear guidance online about how to do this. Any help much appreciated, whether either descriptive, referral to specific intro resources (e.g. not php.net), or with code itself.
I am including relevant scripts below. This one works:
<script type='text/javascript'>
$(document).ready(function(){
$("#search_results").slideUp();
$("#search_button").click(function(e){
e.preventDefault();
ajax_search();
});
$("#search_term").keyup(function(e){
e.preventDefault();
ajax_search();
});
});
function ajax_search(){
$( "#search_results").show();
var search_val = $("#search_term").val();
var search_valb = $("#search_theme").val();
$.post( "./find.php", {search_term : search_val, search_theme : search_valb}, function(data){
if (data.length>0){
$( "#search_results").html(data);
$( ".portfolio_container").hide();
$( ".portfolio_draggables").hide();
$( "ul.clickable_container li").click(function(){
$( ".portfolio_draggables").hide();
var activeImage = $(this).find("a").attr('href');
$( ".portfolio_container").show();
$( activeImage).show();
return false;
});
};
});
};
</script>
This is the html form that I'm using.
<div id="lettersearchform" class = "lettersearchform">
<form id="searchform" method="post">
<label for="search_term">Enter your word here!</label>
<input type="text" name="search_term[]" id="search_term" />
<!--<input type="submit" value="search" id="search_button" />-->
</form>
</div>
<div id="search_results"></div>
The "search_results" are generated successfully with this script:
$alpharray = array();
while($row = mysqli_fetch_assoc($result)){
$alpharray[$row['letter']][] = $row;
}
$first = $second = array();
foreach( str_split( $_POST['search_term']) as $alpha)
{
$first[] = "<li><a href = '#$alpha'><img class='imgs_clickable_droppable' img src='../Letterproject/images/{$alpharray[$alpha][0]['photoPath']}' width='100' height='140'></src></a></li>";
$editable = array("<div id='$alpha' class='portfolio_draggables'>");
foreach ($alpharray[$alpha] as $tempvar)
{
$editable[] = "<a href ='findall.php'><img src='../Letterproject/images/{$tempvar['photoPath']}' width='70' height='110'></src></a>";
}
$editable[] = '</div>';
$second[] = implode( '', $editable);
}
echo '<ul id = "clickable" class="clickable_container">';
echo implode( '', $first);
echo '</ul>';
echo '<div id="portfolio" class = "portfolio_container">';
echo implode( '', $second);
echo '</div>';
So here's where the problem enters: This sortable script is more limited than the cross-div I want, but this type of thing won't work.
$(function() {
$("ul.clickable li").draggable({
containment: 'parent',
revert: 'invalid',
opacity: '0.91',
cursor: 'crosshair'
});
$('.clickable').sortable();
});
For those interested in more context, I'm using jquery to post the input from a form. A Php script matches the characters inputted to a corresponding letter image in a mysql db. It then echoes a list of these images to one div and echoes the whole portfolio of all images of that letter to another div. The idea is that a user could drag images from this second output to replace a letter image in the other div.
Thanks so much!
$("ul.clickable li")
does not correctly select an element with an HTML id of clickable and a class of clickable container. Either change the class of the element (which is selected with . in jQuery/CSS) or the id (which is selected with #)
$("#clickable")
should work.
Additionally, /src is unnecessary as src is not a tag. Writing out HTML directly in PHP script is not the cleanest way of doing this. I highly recommend you check out Head First HTML/CSS or HF HTML5 to augment some of your understanding.