I do front-end dev only like 10% of the time and am curious which is the better way to handle making ajax calls. These calls are just posting data to a web app that specifies an action name and an id.
<a href='javascript:addToList({'action':'set-default-time-zone','id':23})'>set default timezone</a>
<div class='add-to-list action-set-default-time-zone id-23'>set default timezone</div>
I have used both over the years but am not sure which one is preferred. It seems like they get to the same point in the end. Would you consider these to be the two best alternatives and is one better than the other?
I've implemented the div method as follows:
$(document).ready(function(){
$('.add-to-list').click(function(){
var id=getId($(this).attr("class"));
var action=getAction($(this).attr("class"));
$.post('/api/' + action,function(data){
...
},'json')
});
});
function getAction(str){
var parts=str.split(' ');
var phrase='action-';
for(i=0; i<parts.length; i++){
var val=parts[i].match(phrase);
if(val!=null){
var action=parts[i].split('action-');
return action[1];
}
}
}
function getId(piece){
var parts=piece.split('id-');
var frag_id=parts[parts.length-1];
var part_id=frag_id.split('-');
var id=part_id[part_id.length-1];
return id;
}
The link method would seem straightforward.
thx
Well the second approach is what you would call Unobtrusive JavaScript. It is believed to be a more robust approach (I'll avoid the term better here.)
However, your implementation is a bit over-complicated. It could be tuned down to:
HTML:
<div class="add-to-list" data-action="set-default-time-zone" data-id="23">
set default timezone
</div>
JavaScript:
$(document).ready(function () {
$('.add-to-list').click(function () {
var id = $(this).attr("data-id");
var action = $(this).attr("data-action");
$.post('/api/' + action, function(data) {
// ...
}, 'json')
});
});
The HTML5 specification allows for attributes starting with data- to be carrying user-defined data. And it's also backward compatible (will work with older browsers.)
Method 1:
<a href='javascript:addToList({'action':'set-default-time-zone','id':23})'>set default timezone</a>
Method 2:
<div class='add-to-list action-set-default-time-zone id-23'>set default timezone</div>
Method 2 is preferred because you would be practicing unobtrusive style of coding with a much clearer separation of your markup and your scripting code. It is alot easier to read and debug, and there for more maintainable. Also, i would propose instead of using CSS classes to pass data, to use the jQuery.data() method to store data on elements.
Related
I'm in the process of getting rid of Jquery from a small project and re-writing the script with vanilla js. In the current code there's a jquery implementation to search a DOM element and then use jquery 'find'to search specific elements within the element.
var ImageCapture ={
cacheDom : function(){
this.form = $('#drawingBoard');
this.saveBtn = this.form.find('#saveBtn');
this.image = this.form.find('#image');
this.results = this.form.find('#results');
}
}
I've converted the above Jquery code into vanilla js like below.
var ImageCapture ={
cacheDom: function () {
this.form = document.getElementById('drawingBoard');
this.saveBtn = this.form.querySelector('#saveBtn');
this.image = this.form.querySelector('#image');
this.results = this.form.querySelector('#results');
}
}
The new implementation seem to be working fine but I wanted to be sure if it's the correct way of replacing the Jquery implementation using vanilla JS?
Thanks in advance.
I've made two jsfiddles to show you a subtle difference in behavior between plain js querySelectorAll and jQuery selectors. You would naturally expect them to behave the same but they do not.
Here is the plain js version: https://jsfiddle.net/a81e2do3/
Here is jQuery: https://jsfiddle.net/a81e2do3/1/
In short, if you have this html:
div#a > div#b > div#c
if you have a plain JS element node object of #b, you can do b.querySelector('#a #c') and successfully select div#c, but you cannot do that in jQuery (which imo makes more sense the jQuery way).
I use jquery autocomplete and it look like my code, to call the plugin, not good. is there any more simple way to call jquery autocomplete
js
$(document).ready(function(){
$("#m_occupation").autocomplete("search/moccupation.php", {
selectFirst: true
});
$("#foccupation").autocomplete("search/f_occupation.php", {
selectFirst: true
});
$("#g_address").autocomplete("search/g_address.php", {
selectFirst: true
});
$("#relationship").autocomplete("search/relationship.php", {
selectFirst: true
});
});
What you've got isn't really terrible. If you're only ever initializing these autocompletes one time, then it's pretty readable overall, although you do have some repetition.
Cache your jQuery objects for future use. In your snippet above, you only reference each jQuery object (e.g., $("#m_occupation") ) once, but in a real webapp, there's a pretty good chance you'll use it more. Caching helps reduce the number of jQuery finding operations, and is a good practice to adopt even though it's not likely to increase performance for a user noticeably.
Cache your options objects. You're repeating your option declaration multiple times; just declare a single autocompleteOptions object and be done with it.
Refactor initialization into a function. If you're really feeling like the autocomplete initialization is ugly, or complex, or subject to frequent edits, make it a single function. Future global edits to initialization can be made one time rather than multiple times.
A redo of your code taking those into account would look like:
var initAutocomplete = function($el, dataUrl) {
var autocompleteOptions = { selectFirst: true };
$el.autocomplete(dataUrl, autocompleteOptions);
};
$(document).ready(function(){
var $m_occupation, $foccupation, $g_address, $relationship;
$m_occupation = $('#m_occupation');
initAutocomplete($m_occupation, "search/moccupation.php");
$foccupation = $('#foccupation');
initAutocomplete($foccupation, "search/f_occupation.php");
$g_address = $('#g_address');
initAutocomplete($g_address, "search/g_address.php");
$relationship = $('#relationship');
initAutocomplete($relationship, "search/relationship.php");
});
You could technically optimize further by using a single string to represent the DOM ID and URL from which you gather the data, but in my experience, that breaks down in maintenance. So I wouldn't couple those too tightly.
I have the following issue i would like to get some help for.
There is a combobox (select) where i choose an item and i get back a dinamic table from php. The table contains example names. Firstname, Lastname and ID(which is hidden). When i click on the table i get the value of the ID of the selected row. So far it is works fine. The problem that the event doesnt want to fire for first. After that it works fine but i need it for first as i have a function which auto click on the first row but this doesnt work until i solve this problem. I made a code which works fine with a html table. But not with the dinamic one. Please help.
Here is the code works fine with dinamic table but just after 2nd click:
function nametableclick() {
var rows = document.getElementById("nametable").rows;
for(var i = 0; i < rows.length; i++)
{
rows[i].onclick = function()
{
data=(this.cells[3].innerHTML);
var data = data;
$.ajax({
type: "POST",
url: "list.php",
data: "data="+data,
Type: "json",
success: function(msg) {
msg = JSON.parse(msg);
$("#dob").html(msg.dob);
$("#age").html(msg.age);
$("#sex").html(msg.sex);
}
});
};
};
};
And here is the code works well but just with html table:
(Actually is same but i use onload)
onload = function() {
var rows = document.getElementById("nametable").rows;
for(var i = 0; i < rows.length; i++)
{
rows[i].onclick = function()
{
data=(this.cells[3].innerHTML);
var data = data;
$.ajax({
type: "POST",
url: "list.php",
data: "data="+data,
Type: "json",
success: function(msg) {
msg = JSON.parse(msg);
$("#dob").html(msg.dob);
$("#age").html(msg.age);
$("#sex").html(msg.sex);
}
});
};
};
$("#nametable tr:eq(0) td:first-child").click();
};
When i use the onload function for the dinamic table it just doesnt work at all.
Thanks for any help in advance.
This question does not suit well for an answer. Instead, I'll do some code analysis.
onload = function() ... - well not terrible but kinda sloppy. Also looks like this is possible a global namespace leak. I'm going to assume this should be window.onload in which case I'd wonder why jQuery's ready event isn't used $(function() { ... }).
var rows = document.getElementById("nametable").rows;
for(var i = 0; i < rows.length; i++) {
rows[i].onclick = function() { ... };
}
Ok now were again running away from jQuery as if it was diseased some how. And then were looping over the array of rows only to construct a new function each time and attach them to the onclick (again avoiding jQuery)? Constructing functions inside a loop is a very bad idea and most linters will complain loudly about that. A suggestion:
$('#nametable tr').on('click', function() { ... });
This will attach the click handler to all the <TR> rows in the table with the id="nametable" attribute.
data=(this.cells[3].innerHTML);
var data = data;
My heart skipped a beat here!. First your pulling out the HTML content into (what I thought was a global variable) until I saw the next line and realized we have variable hoisting. But wait your assigning data to itself. Lastly, the name data doesn't provide any context as to the content of the innerHTML. Since I don't have the data I could only guess so in these examples I'll leave it as data. In the future think about picking names which provide context to their content and use. That way when you read the code you don't have to hunt for what the variables are for or how to use them.
var data = $(this, 'td:eq(3)').text();
Finally, the use of data is to directly concatenate it into a post request. I would assume HTML is not desired in that server API. Not to mention the avoidance of jQuery's parameter building by forcing the data to a string. Instead use a JS object:
$.ajax({
type: 'POST',
url: 'list.php',
data: {data: data} // This is a very poorly designed server API
}).then(function(data) {
...
});
Also, the use of Type: 'json' suggests that your server is not returning proper HTTP headers. First off there is no Type property for jQuery's ajax instead I think you wanted dataType. However the need for a dataType suggests the server is not sending the proper headers. If the PHP script were to return application/json instead of plain/text then jQuery could parse the response for you avoiding the need for JSON.parse on your own which can be a bit error prone.
$("#dob").html(msg.dob);
$("#age").html(msg.age);
$("#sex").html(msg.sex);
Be warned by using html() your directly injecting HTML into the DOM that you received from a third party. This is a big cross site scripting vulnerability. Use text() instead to push data into the DOM unless you know and can assert the trust of your server and the connection to it (SSL to avoid man in the middle). Probably not important for this example but still worth keeping in mind because it's far to easy to have this show up in the wild.
$("#nametable tr:eq(0) td:first-child")
When you have a selector like this it is far easier and readable to instead provide contextual hooks instead of relying on the make up of the DOM. Add things like class="clickable-row" or class="person-data dob" to your HTML markup. It makes for maintenance and readability.
Thanks for the quick reply. Im sure if there are lot of mistakes as i just started to learn this(i mean php html ajax ect.) a few weeks ago so i dont clearly understand everything and i use things i should not use or should do it another way. But there is a simple program i would like to make it done and learn from that. So when i dont know something im trying to get some info (like: w3schools.com) or check other topics which similar what im looking for.
Sorry i left there the
Var data = data;
My mistake. Dont need there. i was trying out something before and left there. Does not make any different anyway.
next:
The onload = function() {
i found in another topic as solved result and it works with a static table but not with dynamic.
I have tried the following. i did not mentioned:
window.onload = nametableclick;
function nametableclick() {
data here
}
But does not work with dynamic table either.
Next:
var rows = document.getElementById("nametable").rows;
for(var i = 0; i < rows.length; i++)
{
rows[i].onclick = function()
{
data2=(this.cells[3].innerHTML);
What it does for me it finds the selected row and comes back with the value of the 3rd(actually 4th) cell which is the ID in my prog. I need this cos i want to sent this value to the php to get all the data from the table where ID = the value. And it works fine.
As i mentioned the prog works fine even if it is not the best way to do it. Slowly i gonna learn how to do it better way. But at the moment the only problem with that is that the dynamic table onclick event fires only after the 1st click.
Thanks and sorry if im a bit hard case. :-)))
Oh 1 more thing:
"First off there is no Type property for jQuery's ajax instead I think you wanted dataType."
For some reason if i type dataType it just does not work at all. I have no idea why. I watched some training videos and read some short courses about ajax and some of them mentioned using dataType some of them just simple type. I followed everything but did not worked for me. i spent like 5 hours another day to find out why actually i have a topics here with that question as well.
get data from mysql with ajax and json into different textareas
And accidently i tried with uppercase T once and it worked. Have no idea why.
I know that puting reference of HTML element into the variable is a good practice if I need to reference to this element many times. But I run into the problem with this while making my project. How can I bind multiple and the same events to the elements which are stored into the variable?
For now I deal with it this way:
var producerEl = $("#js-producer");
var brandEl = $("#js-brand");
var seriesEl = $("#js-series");
bind(seriesEl);
bind(brandEl);
bind(seriesEl);
function bind($el) {
$el.on("keypress", function () {
// some code..
});
}
I need something like $(producerEl, brandEl, seriesEl).on...
var producerEl = $("#js-producer");
var brandEl = $("#js-brand");
var seriesEl = $("#js-series");
producerEl.add(brandEl).add(seriesEl).on("click", function () {
alert('hello');
});
If you are trying to keep your code readable, might I suggest this approach?
$("#js-producer, #js-brand, #js-series").on('keypress', function () { });
Hmm. If you're using these selectors only one, don't care about "I know it is good to". The best solution is the one provided by David Smith.
Anyway, jQuery is using the sizzle selector engine, who has it's own cache. You can ask for
$("#js-producer, #js-brand, #js-series")
the result would be cached and reused.
I've been struggling lately with understanding the best way to organize jQuery code. I asked another question earlier and I don't think I was specific enough (found in this question here).
My problem is that the richer you make an application, the quicker your client side gets out of control. Consider this situation...
//Let's start some jQuery
$(function() {
var container = $("#inputContainer");
//Okay let's list text fields that can be updated
for(var i=0; i < 5; i++) {
//okay let's add an event for when a field changes
$("<input/>").change(function() {
//okay something changed, let's update the server
$.ajax({
success:function(data) {
//Okay - no problem from the server... let's update
//the bindings on our input fields
$.each(container.children(), function(j,w) {
//YIKES!! We're deep in here now!!
$(w).unbind().change(function() {
//Then insanity starts...
}); // end some function
}); //end some loop
} // what was this again?
}); //ending something... not sure anymore
}).appendTo(container); //input added to the page... logic WAY split apart
}; //the first loop - whew! almost out!
}); //The start of the code!!
Now this situation isn't too far from impossible. I'm not saying this is the right way to do it, but it's not uncommon to find yourself several levels down into a jQuery command and starting to wonder how much more logic can add before the screen begins to melt.
My question is how are people managing this or organizing to limit the complexity of their code?
I listed how I'm doing it in my other post...
Just want to add to what was mentioned previously that this:
$.each(container.children(), function(j,w) {
$(w).unbind().change(function() { ... });
});
can be optimized to:
container.children().unbind().change(function() { ... });
It's all about chaining, a great way to simplify your code.
So far, I do it like this:
// initial description of this code block
$(function() {
var container = $("#inputContainer");
for(var i=0; i < 5; i++) {
$("<input/>").changed(inputChanged).appendTo(container);
};
function inputChanged() {
$.ajax({
success: inputChanged_onSuccess
});
}
function inputChanged_onSuccess(data) {
$.each(container.children(), function(j,w) {
$(w).unbind().changed(function() {
//replace the insanity with another refactored function
});
});
}
});
In JavaScript, functions are first-class objects and can thus be used as variables.
Well, for one, having a good IDE that understands javascript can help tremendously, even if just to identify matching demarcations (braces, parens, etc).
If your code starts to really get that complex, consider making your own static object to organize the mess - you don't have to work so hard to keep everything anonymous.
var aCustomObject = {
container: $("#inputContainer"),
initialize: function()
{
for(var i=0; i < 5; i++)
{
$("<input/>").changed( aCustomObject.changeHandler );
}
},
changeHandler: function( event )
{
$.ajax( {success: aCustomObject.ajaxSuccessHandler} );
},
ajaxSuccessHandler: function( data )
{
$.each( aCustomObject.container.children(), aCustomObject.updateBindings )
},
updateBindings: function( j, w )
{
$(w).unbind().changed( function(){} );
}
}
aCustomObject.initialize();
In my opinion the method described by BaileyP is what I use to start off with then I normally abstract everything into more re-usable chunks, especially when some functionality expands to the point where it's easier to abstract it into a plugin then have it specific to one site.
As long as you keep the large blocks of code in a seperate file and coded nicely you can then end up with some really clean syntax.
// Page specific code
jQuery(function() {
for(var i = 0; i < 5; i++) {
$("<input/>").bindWithServer("#inputContainer");
}
});
// Nicely abstracted code
jQuery.fn.bindWithServer = function(container) {
this.change(function() {
jQuery.ajax({
url: 'http://example.com/',
success: function() { jQuery(container).unbindChildren(); }
});
});
}
jQuery.fn.unbindChildren = function() {
this.children().each(function() {
jQuery(this).unbind().change(function() {});
});
}
Somebody wrote a post on the similar topic.
jQuery Code Does not have to be Ugly
For instance, the author, Steve Wellens, suggests to not use anonymous functions, as it makes code harder to read. Instead, push the function reference into the jQuery methods, like so:
$(document).ready(DocReady);
function DocReady()
{
AssignClickToToggleButtons();
ColorCodeTextBoxes();
}
Another takeaway from the article is to assign a jQuery object to a concrete variable, which makes the code look cleaner, less dependent on the actual jQuery object, and easier to tell what a certain line of code is doing:
function ColorCodeTextBoxes()
{
var TextBoxes = $(":text.DataEntry");
TextBoxes.each(function()
{
if (this.value == "")
this.style.backgroundColor = "yellow";
else
this.style.backgroundColor = "White";
});
}
Stick some of the anon functions into global scope functions (or your own "namespace" object), especially the re-used functions, and it begins to look less like what you posted. Kind of like what you linked to.
I described my approach in your other post. Short form:
do not mix javascript and HTML
use classes (basically start to see your application as a collection of widgets)
only have a single $(document).ready(...) block
send jQuery instances into your classes (instead of using plugins)
Use http://coffeescript.com/ ;)
$ ->
container = $ '#inputContainer'
for i in [0...5]
$('<input/>').change ->
$.ajax success: (data) ->
for w in container.children()
$(w).unbind().change ->
alert 'duh'