JavaScript Code Organization Suggestions/Code Review - javascript

I am working on a large site that has a lot of custom (page specific js). There is one main.js and page-specific.js files. Is there a better approach that can use the following pattern?
How can I re-factor multiple methods that use ajax?
I currently assign all onclick events inline such as onclick="MYSITE.message.send... - it there a better way? Creating multiple $("#button").click(function() {}); seems like more work...
var MYSITE = MYSITE ? MYSITE: {};
var MYSITE {
bookmark: {
add: function(contentId, userId) {
var data = {
contentId: contentId,
userId: userId
};
$.ajax({
url: "/rest/bookmarks/",
type: "post",
data: data,
complete: function(response) {
if (response.error) {
alert(response.error);
} else {
alert("success");
}
}
});
}
},
message: {
/* <a onclick="MYSITE.message.send('1234', '1234');" href="javascript:void(0);">BOOKMARK</a> */
send: function(contentId, userId) {
var data = {
contentId: contentId,
userId: userId
};
$.ajax({
url: "/rest/bookmarks/",
type: "post",
data: data,
complete: function(response) {
if (response.error) {
alert(response.error);
} else {
alert("success");
}
}
});
}
}
}

You can use jquery delegate method
if you have div element with a tags
<div id="bookmarks">
BOOKMARK
</div>
$("div#bookmarks").delegate("a", "click", function(){
MYSITE.message.send('1234', '1234');
});
and you can get 'contentId' and 'userId' dynamically.

Firstly, assigning onclick="" directly is bad practice. Using jQuery's click() method is not that much more work, but even if it was, it is cleaner and much more recommended. It keeps your HTML markup separate from your JS functionality and makes it easier to change things later on. That's the very first thing you should refactor.
What I do with my JS is create one main library JS file with all of my classes/core functionality. I then use inline <script> tags on specific pages to call the method that hooks up the links to the functions.
In the global app.js file, I'll have a function:
function initLinksOnPageX() {
$('#button').click(function() { MYSITE.message.send('1234', '1234'); });
/* ... setup any other events ... */
}
In the page body:
<script type="text/javascript">
$(initLinksOnPageX);
</script>
Again, this keeps your markup clear of any JS code, so your JS related updates happen in one file (or fewer). With this setup you shouldn't strictly need page-specific JS files, though organizationally speaking it might make sense-- I don't know exactly how big your JS is.

My thoughts:
This depends on many factors, but if the page specific code is "large" then it is probably best to keep it separate. If, however, it is just a few lines, and we are not talking about thousands of pages, then lumping it all into the main.js may not be a big issue.
Not very familiar with ajax, so I will pass on commenting on how to "refactor".
If you can loop your assignment of the onclick events, then NOT doing them inline is going to save you a lot of work.

I'd consider wrapping my custom functionality up as a jquery plugin.

Related

Triggering a jQuery Re-Draw After Getting Data With Ajax

Good day, all,
Long-time listener, first-time poster...
I have a client who has been promised a seemingly very complex bit of functionality. They want to load the contents of 3 separate pages into one, after a visitor to their site successfully logs in to their account. They want this to happen without a page refresh. Ajax is the solution. I am not, however, experienced with Ajax.
I'm having trouble figuring out how to tell when a $.get command (using jQuery's Ajax commands) has finished loading its content. My approach is to, once the login has been successful, to go and fetch the 3 separate pages, load their XHTML content into variables, and redraw the pages. Below you'll see my pseudo-code. I use "XXItemXX" to stand-in for actual paths. Each resulting page that I'm trying to pull in has a div with class "content" surrounding the data I want to retrieve. The XHTML looks like this:
<html>
<head>
<title>Page Name</title>
</head>
<body>
<div id="header">...</div>
<div class="content">
.
.
.
</div>
<div id="footer">...</div>
</body>
</html>
The jQuery code I've built follows. I'm able to get the form submitted and even get the content back from the various .get commands. The problem is, I can't seem to daisy-chain things as I normally would. I am struggling to figure out how to only fire the jQuery commands to draw the page once all 3 have been successfully retrieved. I'm afraid my biggest stumbling point is how to articulate this when searching with Google to see how others have dealt with this problem. I'm not sure exactly how to describe what I'm trying to accomplish in 10 words or less or in a fashion that will actually return the information I need.
Can anyone help with this? I'm afraid I have too little time and too much to learn.
<script type="text/javascript">
$('XXLoginFormXX').submit(function () {
$.ajax({
type: $(this).attr('method'),
url: $(this).attr('action'),
data: $(this).serialize(),
beforeSend: function() {
$('<div class="loading">Loading...</div>').insertBefore('XXLoginFormXX').css('position','absolute');
},
success: function(data) {
// On successful login, draw page.
$('.loading').fadeOut('slow');
var dr_editProfileXHTML, dr_accountOrderListXHTML, dr_wishListsXHTML;
$.get('XXPathToEditProfilePageXX', function(data1){
var dr_editProfileXHTML = $('div.content', data1);
});
$.get('XXPathToAccountOrderListPageXX', function(data2){
var dr_accountOrderListXHTML = $('div.content',data2);
});
$.get('XXPathToWishListsPageXX', function(data3){
var dr_wishListsXHTML = $('div.content',data3);
});
$('div.content').fadeOut(function(){
$(this).html(dr_editProfileXHTML);
$('XXEditProfileXHTMLXX').before(dr_accountOrderListXHTML);
$('XXEditProfileXHTMLXX').before(dr_wishListsXHTML);
}).fadeIn();
}
});
return false;
});
</script>
Thank you very much for your time, help, and consideration.
Yours,
Sylvan012
If your problem is to wait that all 3 requests have returned, then:
store the results in variables scoped a bit higher so that each of the callbacks can access them
add a variable drawing in the same scope
in each of the callbacks, check if all 3 variables are non-null and drawing is false
if that's the case, then set drawing to true, and do the work
After working on this with people's generous help, I believe I've gotten it. All my thanks to Dave Briand who taught me about .when and .then.
Following is the pseudo-code I came up with. It seems to be working! Sure there's a lot of clean-up to do, but all three of the pages are now being pulled-in! Whoot!
What do you think of my solution?
<script type="text/javascript">
$('XXLoginFormXX').submit(function () {
$.ajax({
type: $(this).attr('method'),
url: $(this).attr('action'),
data: $(this).serialize(),
beforeSend: function() {
$('<div class="loading">Loading...</div>').insertBefore('XXLoginFormXX').css('position','absolute');
},
success: function(data) {
// On successful login, draw page.
var Page01XHTML;
var Page02XHTML;
var Page03XHTML;
$.when(
$.get('XXPathToEditProfilePageXX', function(data1){
var Page02XHTML = $('div.content', data1);
}),
$.get('XXPathToAccountOrderListPageXX', function(data2){
var Page03XHTML = $('div.content',data2);
}),
$.get('XXPathToWishListsPageXX', function(data3){
var Page01XHTML = $('div.content',data3);
})
).then(function(Page02XHTML,Page03XHTML,Page01XHTML){
$('.loading').fadeOut('slow');
$('div.content').fadeOut(function(){
$(this).attr('id','MyAccount').html(' ' + Page01XHTML + Page03XHTML + Page02XHTML + ' ').parents('body').find('.content').each(function(){
dr_thisID = $(this).attr('id');
if (dr_thisID != 'MyAccount') {
$(this).appendTo($('div#MyAccount'));
}
}).parents('div#MyAccount').children().each(function(){
dr_thisClass = $(this).attr('class');
if (dr_thisClass != 'content') {
$(this).remove();
}
});
}).fadeIn();
});
}
});
return false;
});
</script>

How to avoid large "script.js" file, organizing js/jquery code (requirejs)

I always end with huge "script.js" or "main.js" file in my project with one document.ready function and always feel bad about it, but dont know how to organize js/jquery code well on my own. Im familiar with concepts of modules but again dont know how to do it.
So for example, im working on project where i have a many resources(e.g. organisations, users, etc). I will do my best to show minimum of code of what i need to do with every resource:
function deleteResource(url, $that) {
$.ajax({
url: url,
type: "DELETE"
}).done(function() {
$that.closest('tr').remove();
});
}
function emptyHelper(element) {
if(typeof element === "undefined")
return "/";
return element;
}
function createHtml(data) {
var $that = $('.organisations-table tbody');
$that.find("tr").remove();
for(var i=0; i<data.length; i++) {
var element = "<tr>";
element += "<td>" + emptyHelper(data[i].telephone) + "</td>";
element += "<td></td>"
element += "</tr>";
$that.append(element);
}
}
$("tbody").on('click', 'a.delete-organisation', function() {
deleteResource(/*some_url_for_deleting_resource */, $(this));
});
$('#organisation-search').submit(function(event) {
event.preventDefault();
$.ajax({
url: $(this).attr("action"),
type: "GET",
data: $(this).serialize()
}).done(function(data) {
createHtml(data);
});
});
Now imagine that i need to do similar things with 10 resources and to put that code in the same script, how to fight against that, how to encapsulate and organize this example in modules code well?
EDIT 1: After doing some research i can guess that i need to go with requirejs (multipage), like: https://github.com/requirejs/example-multipage. So basically for every resource (organisations, users, blabla1, blabla2, etc) i need to have main.js file?
Can someone provide example-basic concept of code, how to do this using requirejs?
Without requirejs or something similar, you'd have to implement your own module system. What I use when not using requirejs is proper namespacing and then Maven plugins to manually merge all of the JS in the right order. That's more or less how you have to do it when not using requirejs or a competitor.
This is done in two steps
you have to split your big script in smaller pieces.
then use a third party or you're custom implementation to load those files into your html.
require.js is a good one otherwise you may use the plain old <script>, would work just fine.

How to apply the same rules defined in a JS file, containing all rules within document.ready(..) to new posts?

I have a simple JS file that uses Jquery to apply rules to the loaded page.
I starts with the traditional $(document).ready(function(){
Nevertheless, when I load more posts (load more button) or submit a new post, those rules don't apply. I think I understand why...though it is not clear.
Is there a way to apply the same rules to each new added post? Is the only way defining events directly on the html code like e.g onclick....?
I may be a very simple question. I'll appreciate any answers :)
Thanks
JS Code
$(document).ready(function(){
(...)
$('button#cancel').on('click',function () {
$(this).parents('.footer').hide();
$(this).parents('.footer').siblings('.small-textarea-main-feed').removeClass('set-large');
$(this).parents('.footer').siblings('.small-textarea-main-feed').val('');
});
(...)
}); closes all
I am using the following code in load_questions.js to load a new post:
$('form.ajax').submit(function() {
//
var that = $(this),
url = that.attr('action'),
type = that.attr('method'),
data = {};
that.find('[name]').each(function(index, value) {
var that = $(this),
name = that.attr('name'),
value = that.val();
data[name] = value;
});
//event.preventDefault();
$.ajax({
url: url,
type: type,
data: data,
cache: false, // it will force requested pages not to be cached by the browse
success: function(html){
console.log(html);
$("ol#list-feed").prepend(html);
$("ol#list-feed li:first").slideDown(600);
document.getElementById('set-width1').value='';
document.getElementById('tags').value='';
if ($("ol#list-feed > li").size() <= 3) {
$('#loadmorebutton').hide();
} else {
$("ol#list-feed > li:last").remove();
$('#loadmorebutton').show();
}
}
});
//event.preventDefault();
return false;
});
I want that this type of rules apply to new posts I submit.
The DOMDocumentReady event fires exactly once on the page, when the entire HTML document has been loaded and parsed, so any DOM element you should be able to expect be on the page, will be.
Any DOM elements you add to the page from this point on, need to be decorated again. This can be as simple as calling the DOMDocumentReady handler again, in which case you'd want to make it a named function, and pass that named function to $(document).ready(...). Something like this:
var onReadyHandler = function() { };
$(document).ready(onReadyHandler);
$.ajax({
success: function(html) {
$("ol#list-feed").prepend(html);
onReadyHandler();
}
});
Now, it's likely that a better way of handling this (it's really unclear to me what precisely you're trying to accomplish, but that's not a real problem), is to not bind anything to your new posts at all. If you're concerned about events, bind the events to the container you know will be on the page, using 'event delegation' (jQuery link: http://api.jquery.com/delegate/). This pattern takes advantage of the fact that events 'bubble' in the DOM, meaning you can listen higher in the DOM then the elements you actually want to respond to, and just check that the click event happened on the event you do care about ($.delegate does this check automatically). The end result? You bind far fewer event handlers, since you're not decorating each post individually.

Listening for the addition of certain elements to the DOM by AJAX

I have a web application which uses a lot of AJAX to display pages.
In my javascript I have a feature which gets all the elements that have a certain class (testClass). It does a bunch of stuff with these classes but that's not necessary for my problem.
At the moment my function runs when the DOM is ready and it works great. However, I need my function to run when AJAX returns a new page to the browser as it could contain elements with testClass.
Is there a way I can listen if a certain DOM element is added? I basically need a way to recognise a DOM change, when this change has happen run my function.
Or is there a way I can listen for the addition of elements with class testClass?
If it help here is a snippet of my code:
execute = function () {
var found = false;
$('.testClass').each(function () {
//bunch of code
});
}
$(document).ready(function () {
execute();
});
Try with ajax success method
$.ajax({
url: 'ajax/test.html',
success: function(data) {
$('.result').html(data);
alert('Load was performed.');
}
});

Do ajax requests works if JavaScript is disabled in the browser?

I am developing a web application and am using jQuery to provide a good user interface for users. Therefore, I am using ajax requests and many jQuery functions.
If I disable JavaScript in the browser most of the function will not work because I am sending asynchronous ajax requests for many functions. But how can I handle this? Do I need to rewrite the code without using jQuery and ajax?
Find a below a sample button click event:
$("#renameCategory").live('click', function (event) {
if ($.trim($("#CategoryNewName").val()) == "") {
alert("Please enter a category name");
return;
}
var selectedCategory = $("#SelectedCategoryId").val();
var newCategoryName = $("#CategoryNewName").val();
var postData = { categoryId: selectedCategory, name: newCategoryName };
$.ajax({
type: "POST",
url: '#Url.Action("UpdateCategoryName", "Category")',
data: postData,
dataType: "json",
success: function (data) {
$('#' + selectedCategory).text(newCategoryName);
$("#selectedCategoryText").html(newCategoryName);
},
error: function () { alert('error') }
});
});
How can I handle this?
Ajax requests and jQuery will not work when the client has JavaScript disabled. The best way to make this work is to use the URL from the <a> tag href like so:
Click Me!
$("#renameCategory").on('click', function (evt) {
//To prevent the link from sending the default request
//call preventDefault() on the jQuery event object
evt.preventDefault();
//
if ($.trim($("#CategoryNewName").val()) == "") {
alert("Please enter a category name");
return;
}
//GET THE URL FOR THE AJAX REQUEST
var actionUrl = $(this).attr('href');
//
var selectedCategory = $("#SelectedCategoryId").val();
var newCategoryName = $("#CategoryNewName").val();
var postData = { categoryId: selectedCategory, name: newCategoryName };
$.ajax({
type: "POST",
url: actionUrl,
data: postData,
dataType: "json",
success: function (data) {
$('#' + selectedCategory).text(newCategoryName);
$("#selectedCategoryText").html(newCategoryName);
},
error: function () { alert('error') }
});
});
You will also need to check for ajax requests in your Controller like below:
public ActionResult UpdateCategoryName() {
...
if(Request.IsAjaxRequest()) {
return Json(yourData);
}
return View();
}
This way, if your user has JavaScript disabled, the link will function like a normal HTTP request. If the user has JavaScript enabled, then they will get the Ajax experience. This is called graceful degradation.
Ajax call works when javascript is enabled.
You can handle it by server-side scripting, when javascript is disabled, you must do works by post/get requests, so you have to recode your web application.
If a lot of modification is needed for your website to work without javascript, then just force the users to enable javascript. One way to notify users to enable javascript is to use the noscript tag. http://www.w3schools.com/tags/tag_noscript.asp
View stackoverflow's page source to see how they use noscript
If JavaScript is disabled in the browser, the <script> tags won't be interpreted and executed in your document, including all your jQuery and AJAX JS code. The most common way to implement interactive web application other than Javascript is Flash, so you can still have a backup plan. You can also go with the old-school server side only generated dynamic pages.
Today, however it is very rare for someone not to have JavaScript enabled, so it should not be an issue at all.
Anyway you can make use of the <noscript> html tag to display a message to these users.
<script type="text/javascript">
... Js code ...
</script>
<noscript>You have JavaScript disabled in your browser. Please enable it.</noscript>
Obviously any functionality depending on script will not work if scripting is disabled, not available or incompatible with the environment it is trying to run in.
It is considered by many to be a good strategy to develop web applications so that they work without script support. You can then add scripting to improve the workflow and efficiency, but you will do so knowing that you have a fall back to a working system available if at any point the script should not run.
The discipline of designing and implementing a good workflow based on just HTML and forms may well lead to an easier interface to script and a more efficient workflow.
All too often developers throw together some minimal HTML and CSS, then try and do everything in script. The extreme is to have a DOCTYPE, title element, one block element and one script element that does everything. Not recommended.

Categories

Resources