I have a PHP page with an unordered list. I have a some jQuery code that waits for a user to click on one of the items in the list:
$(function() {
$('li.large_box').css('cursor', 'pointer')
.click(function() {
$('#db_sections').empty();
var show_id = this.id;
$.post('get_section_dates.php', { show_id: show_id }, function(sections) {
$('#section_dates_feedback').html(sections);
});
});
});
When a user clicks on one of these items the jQuery code sends it's id to a php script that makes a database query and builds a dropdown list with the results:
if (isset($_POST['show_id'])) {
$show_id = $_POST['show_id'];
$result = mysql_query("SELECT `id`,`air_date` FROM `daily_show` WHERE show_id = '".$show_id."'");
echo"<div id='dates_select'>";
echo "<select id='date_select'>";
echo "<option value='0'>Choose a date</option>";
while ($query = mysql_fetch_assoc($result))
{
$id = $query['id'];
$air_date = strtotime($query['air_date']);
$date = date("M-d-Y \(D\)",$air_date);
echo "<option value='$id'>$date</option>";
}
echo "</select>";
echo "</div>";
}
The first time I click on one of the list items everything works quickly and the dropdown box comes out correctly. The problem is, when I click on the next list item, the code takes a few seconds longer to build the new dropdown list in place of the old one. Each new click on a different list item compounds the time it takes to build the dropdown list until it takes more then a minute each time.
The database table that it's querying is only 12 records long, and generally it's only returning 1 or 2 rows at the most.
I'm new to PHP/jQuery and was wondering if there was anything blatantly obvious in my code slowing this process down.
Thanks for taking a look at my problem!
you should consider optimizing your transport method as well as your JS that handles it.
First of all, your scripts are building "fat" every click. that is, there are excessive jQuery calls. you can optimize it into this:
$(function() {
//put into reference static elements
$db_sections = $('#db_sections');
$section_dates_feedback = $('#section_dates_feedback');
//delegate event to parent handler using .on()
$('the_containing_ul').on('click', 'li.large_box', function() {
$db_sections.empty();
$.post('get_section_dates.php', {
show_id: this.id
}, function(data) {
//callback
});
});
});
as for your PHP reply, you should at least use JSON for transport and not HTML to make it light. you can use json_encode to turn a PHP array to a JSON string for transport.
if (isset($_POST['show_id'])) {
$show_id = $_POST['show_id'];
$result = mysql_query(query_here);
$resultArray = mysql_fetch_assoc($result)
//do a little formatting before we send over
while ($resultArray){
$resultArray['air_date'] = date("M-d-Y \(D\)",strtotime($resultArray['air_date']));
}
echo json_encode($resultArray);
}
this will print the following JSON string which is better than printing HTML:
[
{"id":"1","air_date":"January 1, 12"},
{"id":"2","air_date":"January 2, 12"},
{"id":"3","air_date":"January 3, 12"},
... and so on
]
now, in the POST request, jQuery intuitively converts it into a JSON object which we can parse. you can now use it to generate your selectbox. this will be the callback:
function(data) {
//create a select and div, and append it to a div
$div = $('<div id="dates_select" />');
$select = $('<select id="date_select" />').appendTo($div);
//create options based on data and append to select
$.each(data,function(index,row){
$('<option />')
.attr('value',row.id)
.text(row.air_date)
.appendTo($select);
}
//put div into the feedback
$section_dates_feedback.html($div);
}
Sounds like a multiple binding issue to me. Just a guess, but maybe you are adding the click function to the clickable item each time the POST returns. So, the next time you click, it's going to run the POST twice. Then three times. That's adding a new request each time as well as adding redundant DOM manipulations each time.
However, the code as it looks in the sample isn't showing obvious signs of having the click bound again. So even though I have a suggestion, it's sort of a shot in the dark.
(Side note: there's no reason to add the CSS via jQuery here. Just put this rule into your style sheet! I've eliminated it from the sample)
Now, I don't know your markup, so I'm going to play safe and use document as my suggested listener to help avoid multiple binding. Basically, the first selector in here is ideally an actual node that never gets destroyed as part of the Ajax call. It's rendered on first page load and then never rendered again. You might use $('#someContainer').on( /* ... */) for example. I'll just use document.
$(function() {
$(document).on('click', '.large_box', function() {
/* all the stuff that should happen on click */
});
});
Now here's the truly important thing that you SEEM to be doing in your sample but may not be. This document ready function must be fired only once when the page first loads. A true document ready function as it was intended to be. Don't put it inside any functions that might get fired.
Quick way to tell if it's being bound multiple times is to open Firebug or Webkit Developer Tools... you'll see the POST HTTP requests. If multiple are getting fired for one click, just figure out why it's being bound again.
Related
i am tying to build an application where user can reorder items (and save the order to database). The items user is reordering are navigation links, which are generated dynamically on the page from php loop:
$nav_links.='<li class="collection-item ui-state-default item" data-ord="'.$navorder.'" data-url="'.$pageurlname.'"><a>' .$pagename. '</a></li>';}
$navorder is order of the page in the navigation
$pageurlname is string which is used to call the page dynamically (index.php?page=$pageurlname) and is unique key in the table.
I am using jqueryUi sortable funcion to make the process drag & drop, it is working fine and each time i reorder the links, the new order is updated to "data-ord".. the sript:
$('#sortable').sortable({
stop: function(event, ui){
$(".sortable li").each(function(i, el){
$(el).attr('data-ord',$(el).index()+1);
});
}
});
Now to the problem, which is my ajax script:
$(document).on('click','.saveorder',function(){
var neworder = $('.collection-item').attr('data-ord');
var pgurl = $('.collection-item').attr('data-url');
$.ajax({
type:'POST',
dataType:'text',
url:'/rs/pages/nav_order.php',
data: { neworder:neworder, pgurl:pgurl },
success: function(data) {console.log(data); $('#response').html(data);},
error: function(data) {console.log('Error!', data); }
});
});
I am new to ajax, so it is mostly build on scripts i found in other quiestions here. (I was able to succesfully implement cript link this to my other functions) however it is not working in this case. The problem seems to be that i am trying to post multiple data for multiple rows (At this time i have 4 links i am trying to reorder, but the link count can be more or less). When i tried to get values of variables "neworder" and "pgurl" (using alert), it always show only the values for the first item.
I have tried lot of solutions found in similar quiestion but none of them worked, simply because user were posting form data and then serialized it, which is not my case because i am not sending data from the form.
Lastly here is the nav_order.php (i guess it is wrong here too, probably need to add foreach but at first i need to have the ajax working correctly):
<?php
include "/rs/include/db.php";
$neworder = $_POST['neworder'];
$pgurl = $_POST['pgurl'];
$query = mysqli_query($Connection, "UPDATE horus_pages SET nav_order='$neworder' WHERE url_name='$pgurl'") or die (mysqli_error($Connection));
echo 'Reordered';
?>
Also when i check the console, there is no data.
So please can you tell me how to correct the ajax script to send the data for each object and then handle it correctly in the php script? Hope i described my problem clearly. Thank you for any help.
Put data-id="your_database_id" in your links html. Selecting them in your database with href, will be slow and bug things if there are multiple records with the same href.
You have save button which sends the order and href of the first link it finds? And what happens when multiple items change their order? If you have many links, you will be throwing hundreds of mysql updates for each save?
You should be better off sending json to your php. Something like that:
[{id:123, order: 332}, {id:124, order:334}, ... ]
dataType:'text' becomes dataType:'json'
If you don't care about scalability, then this will work on the backend.
$data = file_get_contents("php://input");
$links = json_decode($data, true);
foreach($links as $link) {
$order = intval($link['order']);
$id = intval($link['id'])
// UPDATE table SET `order` = '$order' WHERE id = '$id'
}
Btw. Your php code allows SQL injection. Thats bad
Perhaps you can make the order 'float' and make an algorithm which finds empty spot. This will allow you to reduce these 100's of SQL requests to 1.
I am looking to display the total number of files in a database. To clarify, say I had a website where people could upload pictures of their cars, and I wanted to display a live number of how many pictures there are, what would be the best way to do this? Javascript, php? A mix? I envision a div with a number saying "Total Pictures: x" and where x would be whatever the live total is. I plan on using MySQL to store all the data on the website. Is this even recommended to have something communicate with the server this much? Is there a name for displaying a live number? Thanks!
If you are thinking to use the AngularJS way, you could create a Poller service which polls every second (assuming your /counter.php returns json):
app.factory('Poller', function($http, $timeout) {
var data = { response: {}};
var poller = function() {
$http.get('/counter.php').then(function(r) {
data.response = r.data;
$timeout(poller, 1000);
});
};
poller();
return {
data: data
};
});
Then your controller:
app.controller('CounterCtrl', function(Poller, $scope){
$scope.counter = Poller.data;
});
And finally in your view:
{{counter.response}}
You can read more about $http
Set up a PHP script that queries the database and returns the total file upload count. After that, you can use JavaScript on the page to periodically call the server in a specified interval of time and fetch the count data from your PHP script. Using jQuery and GET, you can do something like this:
jQuery(function($){
setInterval(function(){
$.get( '/counter.php', function(fileUploadCount){
$('#counter').html( fileUploadCount );
});
},20000); // 20 seconds
});
In your HTML:
<p><span id='counter'>xx</span> files have been uploaded so far!</p>
Hope this helps!
How live do you want it to be? Just whenever someone updates the site it's going to have the new value or do you actually want it to update in near real-time?
If it's the latter you have to use Javascript against some kind of API that returns the amount of files in the database. I can't help you with that bit since you are using PHP, but it shouldn't be too hard. Just return some JSON looking something like
{ fileCount: 45020 }
Client-side you have a few options. You have the different javascript frameworks like AngularJS and EmberJS (and many more), as well as just 'plain old' javascript and frameworks like jQuery
The keyword is really AJAX, even if that is just a sort of buzzword for using javascript to make websites dynamic.
I am a fan of using AngularJS because it's easy, but I'll try to give you some pointers for using jQuery first. Note that I have not used jQuery in years now.
The jQuery way
jQuery has a function called jQuery.getJSON(), and according to the documentation you can use that function something like this:
// Assign handlers immediately after making the request,
// and remember the jqxhr object for this request
var jqxhr = $.getJSON( "http://example.com/api/fileCount.json")
.done(function(data) { console.log(data) })
.fail(function() { console.log( "error" ); })
.always(function() { console.log( "complete" ); });
So this means we can call an endpoint and fetch some data using jQuery.
Here is a link to a tutorial about the basics of jQuery by the way.
jQuery makes us able to do things like this:
<div id="divTest1"></div>
<script type="text/javascript">
$("#divTest1").text("Hello, world!");
</script>
When that is executed the div with id "divTest1" will contain the text 'Hello, world!'.
That sounds like something we could use here!
Javascript also has this really nice function called setTimeout(), which allows us to make it call a function later.
This describes how to use jQuery with setTimeout()
As you can see it also shows us jQuery.documentReady(), which is an event that fires when the website is finished loading, so it is a good place to put code we want executed.
The example below shows how to use jQuery to hide a div with id=div after 3 seconds.
jQuery(document).ready(function () {
setTimeout( "jQuery('#div').hide();",3000 ); //hide a div after 3 seconds
});
Combining these things you should be able to make a repeating call that fetches data from your server and then updates a div or another element with the data you have fetched.
Just create a function which uses jQuery.getJSON() to fetch data, and then at the bottom of that add a setTimeout call to run itself in X seconds (however often you want it to update).
In jQuery.documentReady() you call that function the first time the document loads.
And in the .done() bit of the getJSON() call you add the data you got from the server to your div with whatever html you want. I showed you how to use $("#divTest1").text(), but there is also a .html() which acts the same but you should use it to add html to a element.
The angular way would be to use AngularJS's $http to do the same thing, but I wouldn't recommend learning AngularJS until you have a bit of a better grasp on Javascript.
When you do though, I highly recommend it. It's a much better approach than using jQuery.
You can read about AngularJS here
I hope this helps!
I have a form using the TinyMCE Editor running on a LAMP system. I wish to create an autosave feature similar to Google Docs. I have thought of two scenarios however, both will produce an overhead on the server.
Post an Ajax request on keyup
Post an Ajax request every 60s
Obviously the first point is not feasible. Can anyone suggest a better solution to point two?
Edit 1
Ok, so a third option could be a combination of Thariama's answer and my second point.
3) Post an Ajax request every 60s if there is signifcant change eg 10 chars or more
Any advances on this would be much appreciated.
Edit 2
Ok I have prototyped my solution based on point 3. In case anyone is interested my code flows like this:
I am using JQuery. I have a form with a textarea with TinyMCE attached to it and a hidden field to store a count of keystrokes.
tinyMCE.init({
...
// Callback for counting keystrokes in TinyMCE
handle_event_callback : "keyCount"
});
$(function() {
autoSaveContent();
});
// Callback function - Get count, increment it and then set it
function keyCount(e) {
if(e.type == "keypress") {
var count = parseInt($("#keyCount").val());
count++;
$("#keyCount").val(count);
}
}
// Autosave every 10s if there have been over 30 keystrokes
function autoSaveContent() {
var keyCount = parseInt($("#keyCount").val());
if(keyCount > 30) {
tinyMCE.triggerSave();
var formData = $("#programmedItineraryForm").serialize();
$.post("/path/to/save.php", formData, function(data,textStatus) {
if(data.success) {
$("#keyCount").val(0);
}
});
}
setTimeout('autoSaveContent()',10000);
}
Hmm, there are many options.
Point 1 is indeed a bit too expensive.
I think it would be a good idea to send a request based on the number of new characters entered in the editor. For example send a request every 5-10 new characters.
You can increment a counter onKeyDown or onKeyUp and reset that counter when a request gets send.
tinymce.init({
plugins: "autosave"
});
I am using jQuery for my Ajax calls... I have x amount of Ajax calls that append to a div. These Ajax load requests are generated by a PHP foreach loop... The problem is they render out of the order; they are set in the array...
<script type="text/javascript">
function loadPage(target, url, append)
{
if (append == true) {
$.get(url, function(data) { $(target).append(data) });
}
else {
$(target).load(url);
}
return false;
}
</script>
////// ----- PHP
<?php
$this->data['sidebar'] = array('login', 'active_leagues', 'latest_forum_threads', 'latest_matches', 'sponsors');
if (isset($sidebar[0]) && !empty($sidebar[0]))
{
echo '<div class="right_col">';
foreach($sidebar as $val)
{
echo "<script>loadPage('.right_col', 'http://dev.banelingnest.com/sidebar/". $val ."', true)</script>";
}
echo '</div>';
}
I am wonder if the cause of this is the web server responding slower to some requests than others... Other than that, I have no clue why this could be happening. Do you have any thoughts how I could keep the requests in order?
You have to create reference points before the requests, and append the results to them:
var counter = 0;
function loadPage(target,url,append)
{
if (append == true) {
var id = "container_"+counter;
$(target).append("<div id='"+id+"'></div>")
$.get(url, function(data) {
$("#"+id).append(data);
});
counter++;
} else {
$(target).load(url);
}
return false;
}
Your reference elements will be appended to the target on every loadPage() call, so they will be in the correct order, and the request can come in any order they will be loaded in their right place.
This is happening because the ajax calls are asynchronous, and the order they go out has nothing to do with the order they are returned. They will all happen independently and it's expected for some to run faster than others.
You will need to use $.ajax instead of $.get, and set async to false.
See this question: How can I get jQuery to perform a synchronous, rather than asynchronous, Ajax request?
You can also use the unique and interesting solution presented by #inti.
You could do synchronous requests instead of asynchronous, which'd force the browser to wait until each individual request finishes before starting the next. The downside that is for any "lengthy" requests (or many short ones), the browser will be locked up.
You may want to investigate sending all your requests in a single AJAX call, rather than doing one-request-per-call. That way it'd be easy for the scripts on both sides to keep everything in order. Otherwise you're stuck depending on the user link to your server having low error rates, low latency, and low congestion.
So instead of doing the equivalent of
loadPage(1); // fetch data #1
loadPage(37); // fetch data #37
loadPage(203); // fetch data #203
do something like
loadPage([1,37,203]); // fetch all 3 at once.
I have 2 ideas that may help, the first is:
jQuery has a $(document).ready(function() function that is possibly being called from a parent function or being inherited somehow, this means the JavaScript won't run before the rest of the PHP has loaded.
I have seen some functions inherit this from jQuery without it being declared.
The second is:
I am assuming that this function is running in the head or early on in your page and not the foot or later on in the document.
I hope they help.
This is the nature of AJAX, and yes the server is responding faster to some than others.
If you want them in order, you would have to make the first call, then on the complete event, call the next one, and so on; in essence creating a synchronous chain of calls (kind of goes against the A in AJAX).
Without knowing your specific reasons for wanting them in order, this may be a lot more work than what it's worth.
However you do it, it will take away from the user experience, because if one call is slow, all of the other will have to wait.
The simplest solution is creating placeholders, as inti described. Your elements will not necessarily appear in order, but they will end up in the right order. If you need them to appear in order too, here is a simple queue using deferreds:
var queue = [];
function loadPage(target,url) {
queue.push($.get(url));
$.when.call($, queue).then(function() {
$(target).append(Array.prototype.pop.call(arguments));
});
}
The AJAX calls will run in parallel, but the callbacks will fire strictly in order.
Here is what I did with the array or urls I needed to load in order.
I created the order of wrappers first, than did the ajax calls, and load the results into the matching wrapper. This keeps the calls asynchronous, but you still the the proper order.
$.fn.dashboarder = function(options)
{
var settings = $.extend({
urls: [],
}, options || {});
var self = this;
if (settings.urls.length)
{
$(self).html('');
/// create wrapper blocks in the proper order, so they eventually display in this order
$(settings.urls).each(function( index, value )
{
var wrapper = $( "<div />" )
.addClass('dashboard-block-item')
.attr('id', 'dashboard-block-item-'+index);
$(self).append($(wrapper));
});
$(settings.urls).each(function( index, value )
{
$('#dashboard-block-item-'+index).load(value, function( response, status, xhr )
{
}).delay(5000 * index);
});
}
return this;
}
function debug( obj ) {
if ( window.console && window.console.log ) {
window.console.log( obj );
}
};
I have a complicated page being rendered by PHP and would like to keep all elements of the page up to date via AJAX long polling. Is there some kind of general / clever way to design an infrastructure to support this without having to specify manually each element to update? Just looking for ideas. Thanks!
Using jQuery, I would send a comma-separated list of jQuery selectors to update to the server. The server will ultimately respond by reading those selectors and producing the HTML to fill up the elements matching the selectors:
$.get("/updater", { elementsToUpdate: "#someDiv,#someTable,#someOtherElement"}, function(json) {
$.each(json, function(k, v) {
// the key is the selector, the value is the
// HTML to set to that (or those) element(s):
$(k).html(v);
});
}, "json"); // we are expecting the server to return JSON
The server will respond by sending JSON to the client with the following structure:
{"#someDiv":"this is some HTML to fill up div with ID someDiv","#someOtherElement":"here is some more HTML","#someTable":"here is some more HTML"}
maybe add a special class to all the elements to be updated via ajax, and also you can encode extra data in some other attributes for example
<a class="ajaxUpdate" data="{'a':'json or whatever', 'put whatever here':'ok'}">test</a>
then with jquery you can easily pull out that data and eval it, and use it in your ajax
You can poll a page which returns you the json-encoded structure of your page, like:
var page = {
'elem1': {
'html': '<div>... ',
'update': True
},
'elen2': {
'update': False
and so on, then update what necessary, having only one poll.
Keep your pages elements structure as private data and have the public elements to update them
var page = (function(){
var private_data = {
//json
};
return {
workOnData : function(){//process the data}
}
})()
This is good way you can keep your pages data safe and smooth.
I'd use jQuery to do that if I were you o: then you could do something like...
$('div.updateThisClass').css('color','#fff')