How to make this jQuery driven page snappier? - javascript

I have a contact script. It uses jQuery for it's ajax requests and animations.
I also use it with a hashchange plugin to fix the back button. The slow part lies there.
After finishing the animation of the 'flip', the form fades slowly. The browsers seem to block for a sec. I'm trying to make it snappy (no blocking).
Here is the function responsible for handling the hash change event:
handleHashChange : function () {
// Get the name of the object and cache it
var self = this,
// Get the current hash
hash = window.location.hash,
// Cache the form height var
formHeight = '';
// If the hash is #send or #error, don't do anything
if (hash === "#sent" || hash === "#error") {
return;
}
// Change the page title back to the default
if(self.documentTitle && self.documentTitle != undefined) {
document.title = self.documentTitle;
}
// Reset all inputs in the form
self.inputs.val('').removeAttr('checked').removeAttr('selected');
// Get the height of the form
formHeight = self.getHeight(self.form);
// Show the transition
self.showTransition(self.response, formHeight, function() {
// Show the form
self.form.fadeIn('fast', function() {
// Show the button
self.button[0].style.display = 'block';
// Focus the first input
self.inputs[0].focus();
})
})
}
The whole code can be seen from the link below, it's fully documented:
http://www.coolcontact.co.cc/beta/1.3/js/main.js
You can see I have used a lot of tips I found on the internet to optimize this script, except using javascript's native 'for' in place of '$.each()' , but it's not that big of a deal here.
If anyone wants to see the slowness, try sending an empty message (validation is disabled) from the link below then click the back button in your browser:
(note: it's not in English, but guess it's pretty self-explanatory ^^)
http://www.coolcontact.co.cc/beta/1.3/
So how can I make it more snappy?

I think it's pretty quick already, but here's something I noticed with your code.
This "if" statement is a bit redundent.
if(self.documentTitle && self.documentTitle != undefined) {
document.title = self.documentTitle;
}
The call to "self.documentTitle" will return evaluate to "false" if its value is "undefined", so you don't need the second "self.documentTitle != undefined".
You could just use the follwing instead:
if(self.documentTitle){
document.title = self.documentTitle;
}
Remember, the values false, null, undefined, 0 and an empty string all evaluate to a false boolean value.

Related

Can you use localStorage to store what a user puts in a div contenteditable and a set color?

I made a fairly basic "biomaker" website where the user can edit the card through several <div contenteditable="true">s, and it also uses a simple javascript to allow the user to cycle through several preset colors.
After receiving feedback from several users it seems that it would be better if the page could save the user's previous information upon closing/refreshing the tab.
I did a little research and I came upon the localStorage function but I'm not sure if it can be applied to the color changer and more importantly, the <div contenteditable="true". I'm wondering if 1) it's possible and 2) if so, how I can make it save the content, since what the user puts in the div doesn't affect the backend.
Thanks in advance for any help.
Disclaimer: I've seen a lot of people bashing others because they're asking for "free code". I'm not asking for that here, I'm just hoping people can 1) tell me if it's possible and 2) can point me in the right direction.
EDIT: Thanks for the help! I was able to find a solution.
Yes it is posssible.
You first should check whether localStorage is available in the user's browser, for what you can use this function that works in all browsers (i use it too on my website):
function storageAvailable(type) {
var storage;
try {
storage = window[type];
var x = "__storage_test__";
storage.setItem(x, x);
storage.removeItem(x);
return true;
}
catch (e) {
return e instanceof DOMException && (e.code === 22 || e.code === 1014 || e.name === "QuotaExceededError" || e.name === "NS_ERROR_DOM_QUOTA_REACHED") && (storage && storage.length !== 0);
}
}
Use it like this:
editableDiv = dopcument.getElementById("print"); // get the editable div
editableDiv.addEventListener("input", function() { // this function will be executed whenever the content of the editableDiv changes
if (storageAvailable("localStorage")) { // check for storage availability
divcontent = editableDiv.innerHTML; // get contents of the editableDiv
window.localStorage.setItem("divcontent", divcontent); // add them to the localStorage
} else { // if localStorage is not supported, notify the user about it
alert("Your browser does not support localStorage or you disabled it, the things you entered could not be saved !");
}
});
and when the user comes back, you can restore the contents:
editableDiv = document.getElementById("print"); // get the editable div
window.addEventListener("load", function() { // this function is executed whenever the page is loaded
if (storageAvailable("localStorage")) { // check for storage availability
divcontent = window.localStorage.getItem("divcontent"); // get the saved divcontent
if (divcontent === null) { // if the user never modified the divcontent before (or cleaned his localStorage), do nothing
} else {
editableDiv.innerHTML = divcontent; // write it into the editableDiv
}
} else { // if strorage not available, notify the user
alert("Your browser does not support localStorage or you disabled it, your previous work could not be restored !");
}
});
Feel free to modify this code to your needs, and notify me about any problems you have with it ! (couldn't test it, am busy right now)

How to restrict user to navigate back to previous pages and refresh?

Currently I am trying to build a website using ASP.NET MVC5.
I am stuck.
Issue: I want that when user goes to a particular page he/she should not be able to refresh the page, go back to the previous page, copy anything, print screen.
Have tried different solutions like the followings:
window.onload = function () {
if (typeof history.pushState === "function") {
history.pushState("jibberish", null, null);
window.onpopstate = function () {
history.pushState('newjibberish', null, null);
// Handle the back (or forward) buttons here
// Will NOT handle refresh, use onbeforeunload for this.
};
}
else {
var ignoreHashChange = true;
window.onhashchange = function () {
if (!ignoreHashChange) {
ignoreHashChange = true;
window.location.hash = Math.random();
// Detect and redirect change here
// Works in older FF and IE9
// * it does mess with your hash symbol (anchor?) pound sign
// delimiter on the end of the URL
}
else {
ignoreHashChange = false;
}
};
}
}
<script type="text/javascript">
window.onbeforeunload = function() {
return "Dude, are you sure you want to leave? Think of the kittens!";
}
</script>
<script type="text/javascript">
function disableF5(e) { if ((e.which || e.keyCode) == 116 || (e.which || e.keyCode) == 82) e.preventDefault(); };
$(document).ready(function(){
$(document).on("keydown", disableF5);
});
</script>
but nothing seem to work.
Please suggest.
You can do this with javascript using the parameters of window.open, for example
window.open('yourUrl','windowName','toolbar=no');
However I would not recommend this. Instead (based on your comment to lujcon's answer) your should design your controllers and view models correctly to prevent the issues you have described. For example, if the user has already answered a question, add a flag, then if they trey to post another answer for the same question, you can check the flag and prevent an update/display error message etc.
The only way I can think of to do what you ask with a web application is to make all of your "pages" a single view that loads the results from multiple Action methods using AJAX. If you load everything using JavaScript/AJAX, the browser won't be able to keep track of any page history.
Don't do that! Web based applications means freedom. User should do anything she/he wants. The question should be: how should I design my application to handle back and refresh button correctly (not disabling them). Disabling printing is useless as at any time user can make print-screen...

I need a new way to detect if there has been a change to an elements HTML

Right now im trying to find a way to detect when an elements HTML has changed.
I'm currently trying:
var a, b;
setInterval(function() {
a = $('#chat').text();
}, 150);
setInterval(function() {
b = $('#chat').text();
if (a !== b) {
alert("There has been a new message.");
}
}, 200);​
What I do is every 150 milliseconds I check for the HTML of #chat and then every 200 seconds I check the HTML again and then check if variable a does not equal to variable b them in the future I will so something with that but for right now I just alert something.
You can see it live here: http://jsfiddle.net/MT47W/
Obviously this way is not working and is not very accurate at all.
Is there a better/different to do/achieve this?
Thanks for any help, I've been trying to figure out how to do this a better for about a week now but I just can't find a fix for this and i'm hoping I posted this problem at the right place, and at the right time.
Use a var to store the element's current text then check against it in a setInverval and update the var to store the current text after checking:
var a = $('#chat').text();
setInterval(function() {
if (a !== $('#chat').text()) { //checks the stored text against the current
alert("There has been a new message."); //do your stuff
}
a = $('#chat').text(); //updates the global var to store the current text
}, 150); //define your interval time, every 0.15 seconds in this case
Fiddle
You may as well store the value in the .data() of the element to avoid using globals.
Example using .data():
$('#chat').data('curr_text', $('#chat').text());
setInterval(function() {
if ($('#chat').data('curr_text') !== $('#chat').text()) {
alert("There has been a new message.");
}
$('#chat').data('curr_text', $('#chat').text());
}, 150);
Fiddle
Another approach, to save client's memory, you can just store the number of child divs your #chat element has:
$('#chat').data('n_msgs', $('#chat').children().length);
setInterval(function() {
if ($('#chat').data('n_msgs') !== $('#chat').children().length) {
alert("There has been a new message.");
}
$('#chat').data('n_msgs', $('#chat').children().length);
}, 150);
Fiddle
EDIT: Here's my very final addition, with a DOM mutation event listener:
$('#chat').on('DOMNodeInserted', function() {
alert("There has been a new message.");
});
Fiddle (not tested in IE < 8)
Note: As noted in the comments, even though mutation events are still supported they're classed as deprecated by W3C due to the performance loss and some incompatibilities across different browsers, therefore it's suggested to use one of the solutions above and only use DOM Mutation events when there's no other way around.
Just checking the last chat would improve efficiency and also do what you want. The only way that it would not work is if the same person sends the same message twice - which most likely will not happen.
I hope this would work:
var lastMessage = $('#chat .body').last().text();
function checkMessages(){
var newLastMessage = $('#chat .body').last().text();
if(lastMessage !== newLastMessage && $('#chat .body').last().length > 0){
//message has changed
alert("There has been a new message.");
lastMessage = $('#chat .body').last().text();
}
setTimeout(function(){checkMessages();},1000);
}
checkMessages();
Js Fiddle
Use crc32 or md5 hashing to check whether data has changed. Just get the html content of the div that you want to check, hash it as a string with either crc32 or md5 and you'll get a string that will represent that content. Use a hidden timestamp etc to be sure that multiple messages by one user get a different hash. If you do this in a setInterval callback, you should be good.
Although it is not highly recommended, it is also possible to use Paul Irish's Duck punching method to tie into jQuery's append function - if you know for sure that the append function is how content is being added (demo):
(function($) {
// store original reference to the method
var _old = $.fn.append;
$.fn.append = function(arg) {
// append as usual
_old.apply(this, arguments);
// do your own checking here
// "this" is a jQuery object
if (this[0].id === "chat") {
alert('there is a new message!');
}
return this;
};
})(jQuery);
With this method, no timing loops are needed.

Check if window.history.go successful?

Is there anyway to check if a window.history.go command is successful in changing the window.location or not?
i.e. If I do a window.history.go(-5) when there are only 3 pages in the history stack, the browser will do nothing.
Is there a way to check if that happens and run other code? An error callback, of sorts.
Thanks.
For an immediate response, first you'll want to check history.length to make sure it is at least 6, e.g. to go -5. Apart from that, I think the only way is to use setTimeout and if the script is still running, the callback will be executed.
Not really a JS expert, but if you want to perform some action when the user goes back or forward, you could use URL hashes and trigger some function using the jQuery onhashchange event. This will not give you the position in history, and i'm also not sure about cross-browser compatibility, but it did the job for me so far.
$(window).on('load' function(){
var hash = parent.top.location.hash;
if(hash == '' || hash == '#' || hash == null){
//if none, set a hash and reload page
parent.top.location.hash = '#/some/hash';
parent.top.location.reload(true);//use true if you dont want to use cached items
}
});
$(window).on('hashchange', function(){
do_something(parent.top.location.hash);
});
function do_something(hash){
//this function will be executed each time the '#' changes
console.log('hash changed to '+hash);
}

Global variable isn't holding?

This should be a really easy "derp" question, but here it is:
I'm trying to set up a global variable in a JS file so that I can control when an action triggers. In my case, I want okBoxCall to only be called if firstTime is true. I have firstTime set to true initially, then I change it to false afterwards. My code is NOT working as it should however, as it still calls up okBoxCall more than once.
var Dialog;
var HUDWindow;
var smartPhone;
var firstTime = true;
$(document).ready(function(){
smartPhone = new SmartPhone();
initDialog();
initHUDWindow();
if(firstTime == true)
{
okBoxCall("Tutorial", "Welcome to McLarin Energy!");
firstTime = false;
}
});
What am I doing wrong? Obviously firstTime is not holding its change to false...
EDIT Forgot to mention that this is for a 3D game, not web pages. Cookies are not used.
I'm guessing you want to check whether this is the first time the user opens a page and open a tutorial if it is?
It is not possible the way you want to do it. Every time your page is loaded your script is evaluated again. So this means a variable firstTime is created and it is set to true. What you need is some persistent storage on the client to store whether it is the first time or not. You will need to set a cookie or call the localStorage API if you don't bother disregarding older browsers.
Your function should only be called once due to $(document).ready(...). So, I'm guessing you're reloading the page to get the alert to display again and again...
Maybe you should be looking at using cookies, not just a plain old JS variable..?
What is okBoxCall doing? If you have any error in okBoxCall firstTime = false will not be executed. Set the value before you call okBoxCall.
$(document).ready(function(){
smartPhone = new SmartPhone();
initDialog();
initHUDWindow();
if(firstTime == true)
{
firstTime = false;
okBoxCall("Tutorial", "Welcome to McLarin Energy!");
}
});

Categories

Resources