I'm creating a dynamic todo list. The list is downloaded from a local database and displayed in a table. At each todo there's a submit-button which you can press and the todo is supposed to register in the database as "done". In my code this is done through a variable called status in which a value of 1 is done, and a value of 0 is undone.
My problem is that you can press any button and all works well; new status is sent to a PHP script, which in turn modifies the status value in the database. Then the webpage automatically updates the display table. But if you try once more, on any of the other buttons, it doesn't work. You'll have to reload the webpage for any of the other buttons to work.
Here's my Jquery/AJAX code:
$(document).ready( function() {
$('#todo_display_table input[type="submit"]').click(function(evt) {
evt.preventDefault();
var todo_id = $(this).val();
//document.write(todo_id);//debug
$.when(changeTodoStatusTo(1, todo_id)).then(updateTodoDisplay);
});
});
function updateTodoDisplay() {
$.post("./daily_todo_display.php", null, replaceTbodyHTML);
}
function replaceTbodyHTML(data) {
$('#todo_display_table tbody').html(data);
}
function changeTodoStatusTo(newStatus, todo_id) {
//Send til phpscript som lagrer ny status i databasen
var parameters = {
todo_id: todo_id,
newStatus: newStatus
};
return $.post("./daily_todo_change_todo_status.php", parameters); //, printDebugInfo);
}
I can post my PHP-scripts as well, but I have tested these separately and they seem to work. Again, all of the functionality on my page works fine, but they seem to stop working after you clicked one time.
I have checked the database and the status value only update itself the on first try(first click of any of the buttons), which would indicate that the problem lies in either the click() function or in the changeTodoStatusTo() function. Thnx for any help, and don't hesitate to ask for more information =)
You shouldn't use the live function of jQuery:
As of jQuery 1.7, the .live() method is deprecated. Use .on() to attach event handlers. Users of older versions of jQuery should use .delegate() in preference to .live().
This method provides a means to attach delegated event handlers to the document element of a page, which simplifies the use of event handlers when content is dynamically added to a page. See the discussion of direct versus delegated events in the .on() method for more information.
jQuery live documentation
As the documentation states, use .on() instead. It works the same way as .live() :)
Are you recreating the submit buttons when you update??? It is hard to tell without seeing all of your code. Have you tried changing this:
$('#todo_display_table input[type="submit"]').click(function(evt) {
to this:
$('#todo_display_table input[type="submit"]').live('click', function(evt) {
see this if applicable: http://api.jquery.com/live/
I'm guessing you're losing the click event code when you update the html() of the #todo_display_table. Try live() instead
$('#todo_display_table input[type="submit"]').live('click', function(evt) {
This is a classic case of browser caching. I experienced the same effect awhile back. One way out would be to invoke all your posts as such:
$.post("./daily_todo_change_todo_status.php?id=" + (count++), parameters);
Within your php script, just ignore the id component. By doing it this way, the browser will generate a new string each time and it won't think that it was calling on the older cached page.
Hope it helped :) Cheers!
It sounds like you're facing some issues with the browser caching the response of your POST requests. This is a common problem with some browsers. You should know that browsers cache content based on the URI, not the actual file. In other words, the query string is included. One workaround to avoid caching the requests is to use a slightly different URI each time:
$.post("./daily_todo_change_todo_status.php?t="+new Date().getTime(), null, callbackFn);
By inserting the current timestamp, you're guaranteed to always make a fresh request to the server, instead of the browser just pulling cached data in lieu of a request.
In addition, in your second POST request, you are attempting to return data. This function is designed to return the response and process it inside a callback function, asynchronously, so that you don't block execution on the page.
However, I see you have a comment there that it's for debugging information, so hopefully this is something you'd remove in production.
From the code I can see, your event handling seems to be just fine. However, you are most likely losing the events when replaceTbodyHTML is called. You could just try running the code from the onload again when you update. For example,
function addListeners() {
$('#todo_display_table input[type="submit"]').click(function(evt) {
evt.preventDefault();
var todo_id = $(this).val();
//document.write(todo_id);//debug
$.when(changeTodoStatusTo(1, todo_id)).then(updateTodoDisplay);
});
}
$(document).ready(addListeners);
And then at the end of replaceTbodyHTML add addListeners();
Related
I am using Ajax and hash for navigation.
Is there a way to check if the window.location.hash changed like this?
http://example.com/blah#123 to http://example.com/blah#456
It works if I check it when the document loads.
But if I have #hash based navigation it doesn't work when I press the back button on the browser (so I jump from blah#456 to blah#123).
It shows inside the address box, but I can't catch it with JavaScript.
The only way to really do this (and is how the 'reallysimplehistory' does this), is by setting an interval that keeps checking the current hash, and comparing it against what it was before, we do this and let subscribers subscribe to a changed event that we fire if the hash changes.. its not perfect but browsers really don't support this event natively.
Update to keep this answer fresh:
If you are using jQuery (which today should be somewhat foundational for most) then a nice solution is to use the abstraction that jQuery gives you by using its events system to listen to hashchange events on the window object.
$(window).on('hashchange', function() {
//.. work ..
});
The nice thing here is you can write code that doesn't need to even worry about hashchange support, however you DO need to do some magic, in form of a somewhat lesser known jQuery feature jQuery special events.
With this feature you essentially get to run some setup code for any event, the first time somebody attempts to use the event in any way (such as binding to the event).
In this setup code you can check for native browser support and if the browser doesn't natively implement this, you can setup a single timer to poll for changes, and trigger the jQuery event.
This completely unbinds your code from needing to understand this support problem, the implementation of a special event of this kind is trivial (to get a simple 98% working version), but why do that when somebody else has already.
HTML5 specifies a hashchange event. This event is now supported by all modern browsers. Support was added in the following browser versions:
Internet Explorer 8
Firefox 3.6
Chrome 5
Safari 5
Opera 10.6
Note that in case of Internet Explorer 7 and Internet Explorer 9 the if statment will give true (for "onhashchange" in windows), but the window.onhashchange will never fire, so it's better to store hash and check it after every 100 millisecond whether it's changed or not for all versions of Internet Explorer.
if (("onhashchange" in window) && !($.browser.msie)) {
window.onhashchange = function () {
alert(window.location.hash);
}
// Or $(window).bind( 'hashchange',function(e) {
// alert(window.location.hash);
// });
}
else {
var prevHash = window.location.hash;
window.setInterval(function () {
if (window.location.hash != prevHash) {
prevHash = window.location.hash;
alert(window.location.hash);
}
}, 100);
}
EDIT -
Since jQuery 1.9, $.browser.msie is not supported. Source: http://api.jquery.com/jquery.browser/
There are a lot of tricks to deal with History and window.location.hash in IE browsers:
As original question said, if you go from page a.html#b to a.html#c, and then hit the back button, the browser doesn't know that page has changed. Let me say it with an example: window.location.href will be 'a.html#c', no matter if you are in a.html#b or a.html#c.
Actually, a.html#b and a.html#c are stored in history only if elements '<a name="#b">' and '<a name="#c">' exists previously in the page.
However, if you put an iframe inside a page, navigate from a.html#b to a.html#c in that iframe and then hit the back button, iframe.contentWindow.document.location.href changes as expected.
If you use 'document.domain=something' in your code, then you can't access to iframe.contentWindow.document.open()' (and many History Managers does that)
I know this isn't a real response, but maybe IE-History notes are useful to somebody.
Firefox has had an onhashchange event since 3.6. See window.onhashchange.
I was using this in a react application to make the URL display different parameters depending what view the user was on.
I watched the hash parameter using
window.addEventListener('hashchange', doSomethingWithChangeFunction);
Then
function doSomethingWithChangeFunction () {
let urlParam = window.location.hash; // Get new hash value
// ... Do something with new hash value
};
Worked a treat, works with forward and back browser buttons and also in browser history.
You could easily implement an observer (the "watch" method) on the "hash" property of "window.location" object.
Firefox has its own implementation for watching changes of object, but if you use some other implementation (such as Watch for object properties changes in JavaScript) - for other browsers, that will do the trick.
The code will look like this:
window.location.watch(
'hash',
function(id,oldVal,newVal){
console.log("the window's hash value has changed from "+oldval+" to "+newVal);
}
);
Then you can test it:
var myHashLink = "home";
window.location = window.location + "#" + myHashLink;
And of course that will trigger your observer function.
Another great implementation is jQuery History which will use the native onhashchange event if it is supported by the browser, if not it will use an iframe or interval appropriately for the browser to ensure all the expected functionality is successfully emulated. It also provides a nice interface to bind to certain states.
Another project worth noting as well is jQuery Ajaxy which is pretty much an extension for jQuery History to add ajax to the mix. As when you start using ajax with hashes it get's quite complicated!
var page_url = 'http://www.yoursite.com/'; // full path leading up to hash;
var current_url_w_hash = page_url + window.location.hash; // now you might have something like: http://www.yoursite.com/#123
function TrackHash() {
if (document.location != page_url + current_url_w_hash) {
window.location = document.location;
}
return false;
}
var RunTabs = setInterval(TrackHash, 200);
That's it... now, anytime you hit your back or forward buttons, the page will reload as per the new hash value.
I've been using path.js for my client side routing. I've found it to be quite succinct and lightweight (it's also been published to NPM too), and makes use of hash based navigation.
path.js NPM
path.js GitHub
SHORT and SIMPLE example
Click on buttons to change hash
window.onhashchange = () => console.log(`Hash changed -> ${window.location.hash}`)
<button onclick="window.location.hash=Math.random()">hash to Math.Random</button>
<button onclick="window.location.hash='ABC'">Hash to ABC</button>
<button onclick="window.location.hash='XYZ'">Hash to XYZ</button>
I am aware that I can call PHP scripts after processing an AJAX request, but am I able to do it the other way around?
I am writing a dynamically sized navigation component for a new website and I want to refresh the nav every time a new item is added to the Menu.
In my menu class I am currently thinking of using this approach to achieve the effect:
public static function new_item($label, $link) {
$pos = self::num_items();
$DB = Database::getInstance();
$query = $DB->connection->prepare("INSERT INTO menu VALUES('', :label, :link, :pos )");
$query->execute(array(':label'=>$label,
':link' =>$link,
':pos' =>$pos+=1
));
?>
<script>
function refreshHeader() {
$.ajax({
type: "GET",
url: "my url",
success: function() {
// refresh the header here somehow
}
});
}
</script>
<?php
}
However, every time this runs I see nothing in my console (even when I put a valid url into my function), will I be able to achieve what I want this way or would I be better processing appending the tab to the document via AJAX and then calling the insert method in my Menu class on the success callback?
The only reason I ask is I feel this way may look neater in comparison to the other way of handling it, although I may be completely wrong.
Any feedback would be greatly appreciated - cheers
Alex.
echoing the following line can call out existing javascript functions and you are even able to insert variables (only strings and ints though).
echo "<style onload='jsfunction(\"$vars\")'></style>";
I find this one of the most simple ways to do this quickly.
Oh, I now understand what you want to do, what you need is websockets. When a piece of php code (sever side) is executed, you want something to happen in the browser (client side). Take a look at how live chats are made with javascript and websockets, long polling and other similar methods, that's what you need here.
This helped me a lot What are Long-Polling, Websockets, Server-Sent Events (SSE) and Comet?
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 am creating a dynamic todo-list on a webpage. On the page you have a form for registering todo's and a table showing all the registrated todo's. The idea is that you register something you want done in a form, hit the submit button, and then the todo-list-table is automatically updated with the latest registered todo. My script manages all of this except for automatically updating the latest registered todo.
Here's my code:
$(document).ready( function() {
$('#todo_registration input[type="submit"]').click(function(evt){
evt.preventDefault();
var todo = $('#todo_registration input[name="daily_todo"]').val();
$('#todo_registration input[name="daily_todo"]').val(null);
$.when( registerTodo(todo) )
.then (
updateTodoDisplay()
);
});
});
function updateTodoDisplay() {
$.post("./daily_todo_display.php", null, replaceTbodyHTML);
}
function replaceTbodyHTML(data) {
$('#todo_display_table tbody').html(data);
}
function registerTodo(todo) {
var parameters = {
daily_todo: todo,
registration_button: 'clicked'
};
$.post("./daily_todo_registration.php", parameters); //, printRegistrationStatus);
}
I have checked that the script successfully registrates the todo in the database. The php-script that gets the updated todo-list also works. My problem, I think, is that the function updateTodoDisplay() doesn't wait for the AJAX call in registerTodo() to successfully complete before it runs. But I thought my use of #.when() was supposed to make updateTodoDisplay() wait.
I know making the AJAX call synchronous would probably fix my problem, but in my opinion that is a bad solution. I only want this one and only function to wait for the AJAX call to complete. Thus I want the rest of the webpage to function while these calls are made.
Any one know a fix for my problem? Thnx.
What you need is possible, but it looks like you have an error in your code.
Change the
.then (
updateTodoDisplay()
);
to
.then (function(){ updateTodoDisplay(); } );
or even
.then (updateTodoDisplay);
The problem is that when you are registering the callback, in your current code you are passing the result of executing updateTodoDisplay() instead of passing it as a function. That is why you get it executed right away.
You should $.post your data, AND when server-side updating is done, respond from server-side too - sending text/json/xml back to the UI. You save one (the second) request with that, you keep ajax asynchronous, you keep your code shorter/more-maintainable, and you get rid of this issue. =)
$.post("url/todo.php", params, function (data) {
// callback
// do UI update here
// "json" but you can say "xml" too
}, "json");
All you need to do is to figure out your server-side response.
jQuery.post()
Have a nice time implementing! =)
how can you be constantly sending data through an ajax request (POST) to a script, and constantly retrieving the results? Id prefer a jquery method but plain ajax is fine too.
I need to send a javascript variable with the height of the page, but obviously theres no windowReSize event, so how can it be set up so that data is constantly being posted and retrieved? Thanks, sorry for the lame question
As Ozair Kafray stated, you'd better use the setInterval method.
By the way, the window resize event does exist.
Also, if you don't mind supporting new browsers only, this "constant ajax stuff" is what WebSockets have been invented for.
You can use setinterval method to do anything after a certain interval.
I am not sure why you want it so, I thought that one of the following thread(s) might be helpful for you.
https://stackoverflow.com/search?q=hash+change+event
you could do this by doing a recursive call in javascript try this code:
<script>
function infinite()
{
setTimeout('infinite()', 50000);
//put any statement here...
jQuery.post(<parameters>);
}
infinite();
</script>
hope this helps. :)