I'm making a blog that takes markdown syntax. I would like to be able to provide an updated preview on every keystroke, just like Stackoverflow does when asking and answering questions. My current solution is incredibly laggy.
How does Stackoverflow implement their solution to provide an updated preview of markdown text without causing lag?
Currently, I make an AJAX call to the server on every keystroke in the post-content text area. The server sanitizes the content and returns HTML. The preview div is then updated with the new HTML:
$('#post-content').bind('input propertychange', (function() {
var content = {"content": this.value};
$.ajax({
...
success: function(data) {
$('#preview').html(data['content']);
}
...
});
}));
This isn't working well. If I type at full speed, the text area ends up missing about 1/3 of my characters. My server is a tiny 500MB RAM VM. I'm not sure if the server is bottle neck, but I don't understand why the textarea would be lagging because of a slow AJAX call - it seems to me that this line is the bottle neck:
$('#preview').html(data['content']);
But I don't know enough about front end development to speculate on this.
How does StackOverflow provide a preview of markdown text, without causing lag?
They store the text locally on your client browser, there is no server or database round-trip
This can be verified by inspecting the code
Don't send an AJAX call at every keystroke to the server! Make the preview use something client-side and only use the server when you submit the post/comment.
Creating the AJAX call is what is causing your lag. I haven't exactly looked but I can promise you that StackOverflow is not making a server call each time to validate and give you a preview of the markdown. They are just using Javascript to do it instead.
Stack Overflow renders the preview client-side, which is why some of the tags aren't sanitized. AJAX calls (especially lots of them) are not neccesary, because most computers can do it just as easily without the server's help.
If you notice, syntax highlighting is not triggered for a few seconds, because it is inefficient to do that every time a character is typed.
Currently, I make an AJAX call to the server on every keystroke
I will not paraphrase other answers and comments that have said it was a bad strategy. That said, if you cannot move your rendering to the client side and if you are ready to lower your expectations in term of speed you can use this solution (mentioned by user1106925 in comments):
define a timer of, say, 300ms
each keystroke resets the timer
when the timer does timeout, make the AJAX call to the server
That should drastically reduce the number of AJAX call. The user do not have to press a "render" button (he only has to stop typing) so it does modify your current UI.
This is the strategy that is used by Compiler Explorer (though not with markdown).
Related
So the whole reason I am using AJAX is to make page changes seem smoother. However I have realized that using AJAX is actually slowing down the website significantly. I am using localhost with apache. I am running php on the backend to access a database for various pages.
It's taken up to 5 seconds just to load a single page.
Here is some AJAX:
$(function() {
$(".menu_nav").click(function() {
$.ajax({
type: "GET",
url: "menu.php",
dataType: 'html',
success: function(data) {
var toInsert = $(data).filter(".placeholder");
var style = $(data).filter("#style");
$("#style").replaceWith(style);
$(".placeholder").replaceWith(toInsert);
window.scrollTo(0,0);
}
});
});
});
'menu_nav' and 'home_nav' are both divs with click events attached to them, and on click they are performing a GET request to the server and asking for a div in the .php as well as it's styling sheet. It then will replace the div and style sheet on this page with what it retrieved from the GET request. Where I am having trouble understanding though is why is this taking up to 5 seconds to perform the GET request, whereas without any javascript I am getting minuscule load times, just less "pretty"?
I looked at the timeline and network tabs in the web inspector, and had noticed that every time I perform one of these requests, I get a new file from the server, rather than reading the one I've already got, which makes sense because there might be new data in the page since the last visit, however I don't see a duplicate being added to the list of sources when I am not using AJAX. For example:
Whereas without AJAX, there is only one. This makes sense since I am initiating a GET request to the server, but the same is happening when you click a link without AJAX.
Regardless, I still don't understand what is making it so slow as opposed to not using JavaScript. I understand it is doing more in addition to just a GET request, but is filtering and replacing text after a response really what is causing this issue?
Side question: This is outside the scope of this question, but in regards to AJAX, when I perform a request to the server, is the PHP within the file still executing before it gives me the HTML? So on pages where a user must have certain permissions, will the PHP that catches that still be ran?
EDIT: I am hosting a MySQL database through a free subscription to a cloud hosting service. This issue occurs when I access my website through both localhost, and when accessing the website that way deployed via the free cloud hosting service, though it is way slower when I use the cloud service. I am also using various resources from the MAMP (MacOS Apache, MySQL, PHP; If you're on windows and interested, WAMP is also available fore free) installation.
I'm not sure what is causing your slowness issues, but you could try doing some profiling to narrow down the issue. My guess is that while changing your code to use ajax, you also introduced or revealed some bug that's causing this slowness issue.
Is there an issue with the javascript? You can place console.time() and console.timeEnd() in different places to see how long a chunk of javascript takes to execute. (e.g. at the start and end of your ajax callback). Based on what you posted, this is likely not the issue, but you can always double check.
Is it PHP that's running slow? You can use similar profiling functions in PHP to make sure it's not hanging on something.
Are there network issues? You could, for example, log the timestamp of when javascript sent the request and when PHP received it, and vice versa. (this should work OK on localhost, but in other environments you have to be careful of clocks being out of sync)
There's a lot that could be going wrong here, so its hard to give a more specific answer, but hopefully that gives you some tools to help you start looking.
As for your side question: you are correct - PHP will start sending the HTML while it continues to execute. For example:
<div>
<?php someLongBlockingFunction(); ?>
</div>
<div> will get sent to the browser, then PHP will stall on the long-running-function before it finally sends out the ending </div>. The browser will piece together the chunks, and your event listener won't get called until PHP has finished sending the entire file.
I currently face the following issue:
After a user has uploaded his images, all images are processed through a script that optimizes every image (compresses it and removes EXIF-data).
I got everything working, the only problem is that the proces takes quite some time. I want to notify the user of the job status, e.g. a percentage of the processed images.
Currently, the user has to wait without knowing what's up in the back-end. What is the best way to accomplish this? I've thought about AJAX-calls, but I honestly have no idea where to start with implementing this, also because it looks like I need multiple calls (kinda like a heartbeat call on the processing job).
The application I am developing in is a Laravel application, I've made an API controller which handles incoming files via AJAX calls.
Any help is appreciated, thanks.
Laravel has Broadcasting for this. It uses websockets, redis or pusher to send events to the client.
This way you can send the client a message when the processing is done without them having to refresh a webpage all the time.
You'd be better off reading about the principle of how it's done, for example: Progress bar AJAX and PHP
Essentially the way it's done is that the job (processing images in your case) happens on the server through PHP. Your script will need to produce some sort of output to show how far through it is, e.g. echo some value for the percentage progress. The PHP script itself is responsible for producing this output, so you must work out how to calculate it and then code that in. It could be that it takes the number of images to be processed into account, and when each one is successfully processed, it adds 1 to a counter. When the counter equals the number of images, 100% done, or possibly some error messages if something went wrong.
On the frontend you could have an ajax script which reads the output from the PHP script. This in turn could update a progress bar, or div with some sort of percentage message - the value used coming from your PHP script.
Laravel - and other frameworks - have built-in methods to help. But you'd be better understanding the principles of how it works, such as on the link I posted.
I programmed an experiment in javascript/jquery extensively using ajax to communicate with the server / execute php scripts on the server.
Now, a friend of mine wants to use the scripts in japan and is running into problems that I haven't encountered before.
For example he has to press submit buttons several times instead of just getting redirected to the next page or he sees placeholder symbols instead of the data that is stored in the mysql database and should have been fetched (and replace the placeholders).
In the last case, the script is quite straightforward. A $.post request is executed in the document ready function, which then executes a function in which the javascript replacements take place.
Now we are kind of lost what could be the cause of it. It seems to work fine on my side.
The server is located in europe and he also had some issues with proxy servers.
Could that be a problem?
Could it be that the $.post commands are just very slow or even time out?
Is there anything that can be done about this to make the scripts run reliably on his side?
I am happy to provide more specific information if needed, however currently I am a little bit lost and don't know what to look for specifically.
Thanks for any help!
edit:
Here is the jquery code for one specific example where we encountered this problem:
function updateContent() {
$.post("php/earnings.php",
{
type: "contribution",
grp : group,
pbnr : PbNr,
multiplier : multiplier
}, processAndShow);
function processAndShow(data) {
// here are just jquery commands updating html elements
}
}
$(document).ready(function() {
updateContent();
});
the earnings.php file contain roughly 150 lines of code with 15 mysql_query requests.
I also had a look at the connection diagnostics in firefox for the earnings.php $.post request:
Connection: "Keep-Alive";
Keep-Alive: "timeout=1, max=96"
has it possibly something to do with the timeout / max settings of the apache server?
All I want is:
select a file
small progress bar (unless it is not simple)
fail/success confirmation on client side
trigger action on server side.
all that without page reloading
Except that, the simpler the better.
Snippet would be most welcome.
There are plenty of scripts and tutorials around. Check for example http://www.ajaxf1.com/tutorial/ajax-file-upload-tutorial.html
Apparently that's not as trivial as one might think, since you can't just take the body of a form, containing an <input type='file'/> tag, and submit that.
But you can submit the form with a target of another <iframe/> and then poll the server with a XMLHttpRequest object for status updates, that however, requires that your sever-side script, that handles the upload, does so in a asynchronous manner, otherwise you will only get a status update once the file has been fully uploaded, not the kind of progress status updates you want. I think this is a challenge for most web frameworks to date, but I have never actually had any reason to dig into it. Sounds fun though...
If you just want to submit the file, independently of the actual form, you'll do the same, but you don't have to worry about the progress status updates.
What you can do, is to replaces the <input type='file'/> once the upload completes, with a <input type='hidden'/> containing the server-side ID of the recently uploaded file. That way you'll know when the user hits save, what files you'll want to actually save.
That hidden thing can also be a checkbox, which would let you undo a file upload, by simply unchecking that checkbox before hitting save.
File uploads using the XMLHttpRequest object is not possible in all browsers (only Firefox and Safari/Chrome support it), so for a cross-browser implementation use the <iframe> trick.
If you want a real XHR file upload, I have written an extended article on how to do it in Firefox 3. It's so low level that you actually have to build the actual HTTP request from JavaScript strings.
Maybe GearsUploader will fit.
Which of the following code is better in building a delete -action for removing a question?
1 My code
<a href='index.php?delete_post=777>delete</a>
2 Stack Overflow's code
<a id="delete_post_777>">delete</a>
I do not understand completely how Stack Overflow's delete -button works, since it points to no URL.
The id apparently can only be used by CSS and JavaScript.
Stack Overflow apparently uses JavaScript for the action.
How can you put start the delete -action based on the content of CSS -file by JavaScript?
How can you start a SQL delete -command by JavaScript? I know how you can do that by PHP, but not by JavaScript.
Your method is not safe as a user agent could inadvertently crawl the link and delete the post without user intervention. Googlebot might do that, for instance, or the user's browser might pre-fetch pages to speed up response time.
From RFC 2616: Hypertext Transfer Protocol -- HTTP/1.1
9.1.1 Safe Methods
Implementors should be aware that the
software represents the user in their
interactions over the Internet, and
should be careful to allow the user to
be aware of any actions they might
take which may have an unexpected
significance to themselves or others.
In particular, the convention has been
established that the GET and HEAD
methods SHOULD NOT have the
significance of taking an action other
than retrieval. These methods ought to
be considered "safe". This allows user
agents to represent other methods,
such as POST, PUT and DELETE, in a
special way, so that the user is made
aware of the fact that a possibly
unsafe action is being requested.
Naturally, it is not possible to
ensure that the server does not
generate side-effects as a result of
performing a GET request; in fact,
some dynamic resources consider that a
feature. The important distinction
here is that the user did not request
the side-effects, so therefore cannot
be held accountable for them.
The right way to do this is to either submit a form via POST using a button, or use JavaScript to do the deletion. The JavaScript could submit a hidden form, causing the entire page to be reloaded, or it could use Ajax to do the deletion without reloading the page. Either way, the important point is to avoid having bare links in your page that might inadvertantly be triggered by an unaware user agent.
Bind a click event on the anchor which start with "delete_post_" and use that to start an Ajax request.
$("a[id^='delete_post_']").click(function(e){
e.preventDefault(); // to prevent the browser from following the link when clicked
var id = parseInt($(this).attr("id").replace("delete_post_", ""));
// this executes delete.php?questionID=5342, when id contains 5342
$.post("delete.php", { questionID: id },
function(data){
alert("Output of the delete.php page: " + data);
});
});
//UPDATE
With the above $.post(), JavaScript code calls a page like delete.php?id=3425 in the background. If delete.php contains any output it will be available to you in the data variable.
This is using jQuery. Read all about it at http://docs.jquery.com/How_jQuery_Works.
The url you are looking for is in the js code. Personally I would have an id that identifies each <a> tag with a specific post, comment... or whatever, and then have a class="delete_something" on each one, this then posts to the correct place using javascript.
Like so:
Delete
<script type="text/javascript">
jQuery('a.delete_post').live('click', function(){
jQuery.post('delete.php', {id: jQuery(this).attr('id')}, function(data){
//do something with the data returned
})
});
</script>
You're quite correct that absent an href="..." attribute, the link would not work without JavaScript.
Generally, what that JavaScript does is use AJAX to contact the server: that's Asynchronous JavaScript and XML. It contacts a server, just as you would by visiting a page directly, but does so in the background, without changing what page the browser is showing.
That server-side page can then do whatever processing you require. In either case, it's PHP doing the work, not JavaScript.
The primary difference when talking about efficiency is that in a traditional model, where you POST a form to a PHP page, after finishing the request you must render an entire page as the "result," complete with the <head>, and with all the visible page content.
However, when you're doing a background request with AJAX, the visitor never sees the result. In fact, it's usually not even a human-readable result. In this model, you only need to transfer the new information that JavaScript can use to change the page.
This is why AJAX is usually seen as being "more efficient" than the traditional model: less data needs to travel back and forth, and the browser (typically) needs to do less work in order to show the data as part of the page. In your "delete" example, the only communication is "delete=777" and then perhaps "success=true" (to simplify only slightly) — a tiny amount of information to communicate for such a big effect!
It all depends on how your application is built, what happens at Stack Overflow is that the delete link click is caught by JavaScript and an Ajax request is being made to delete the post.
You can use a JavaScript library to easily catch clicks on all elements that match your selector rule(s).
Then you can use Ajax to send a request to the PHP script to do the SQL work.
On a side note, ideally you would not use GET for deleting entries, but rather POST, but that's another story.