Options to make jQuery Ajax code more efficient / logical - javascript

I have the following HTML to display content pulled from an ajax script (ajax.php):
HTML
<ul class="list-unstyled" id="var_adjectives"><li><a href='#'>Loading...</a></li></ul>
<button id="37" onclick='update_adjectives();'>Refresh</button>
<hr />
<ul class="list-unstyled" id="var_brands"><li><a href='#'>Loading...</a></li></ul>
<button id="37" onclick='update_brands();'>Refresh</button>
<hr />
<ul class="list-unstyled" id="var_clothes"><li><a href='#'>Loading...</a></li></ul>
<button id="37" onclick='update_clothes();'>Refresh</button>
<hr />
When the page first loads, the following JS is used to populate the list items against the relevant <ul> tag (passing in two parameters each time):
Javascript Page Load
$(document).ready(function(){
$.post('ajax.php',{u:37,n:1} ,function(data){ var this_record = data.output; $('#var_adjectives').html(this_record);},'json');
$.post('ajax.php',{u:37,n:33},function(data){ var this_record = data.output; $('#var_brands').html(this_record);},'json');
$.post('ajax.php',{u:37,n:67},function(data){ var this_record = data.output; $('#var_clothes').html(this_record);},'json');
});
The refresh button can be used to refresh the content in the relevant <ul> tag, calling the following relevant JS function, from the onclick event on each of the 3 buttons:
Javascript Refresh Functions
function update_adjectives() {
$.post('ajax.php'
, {u:37,n:1}
, function(data){ var this_record = data.output; $('#var_adjectives').html(this_record); }
, 'json')
};
function update_brands() {
$.post('ajax.php'
, {u:37,n:33}
, function(data){ var this_record = data.output; $('#var_brands').html(this_record); }
, 'json')
};
function update_clothes() {
$.post('ajax.php'
, {u:37,n:67}
, function(data){ var this_record = data.output; $('#var_clothes').html(this_record); }
, 'json')
};
As you can see, there is a lot of overlap in the basic design of the JS.
I have these questions:
I am stuck working out how I can end up with one single line in the block of JS used to populate content when the page first loads.
I'd like to only have 1 function used to refresh content - because in my example above I have 3 blocks, but in my real page I have about 30 blocks.
While the JS is created by the PHP code when the page loads (rather than me writing it long-hand), it still would be nice to have much cleaner code which avoids having e.g. 30 refresh functions and 30 lines of code to populate each of the different <ul> IDs when first loading the page.
In each case, I can see I would need to pass an ID of relevant <ul> but I am tied up in knots working out if I can achieve what I'm trying to do.
Probably there are many things wrong with using the onclick event too!
Any advice would be much appreciated, thank you.

The most likely blocker is your API endpoint design. According to what you posted, you must access one category at a time and that must be done by sending the {u:N,n:N} combo object as the body of the POST request.
It would simplify things greatly if your endpoint accepted a different object. Something like {category:'name'} would allow greater flexibility.
You could use {category: 'all'} for the initial view of all categories and use {category: 'clothes'} for individual categories for the update/refresh.
Extending that to the click of the refresh buttons. You can use a single event handler and event bubbling to deal with every button click.
First you would add the event handler to containing element for all the <ul> elements.
Given this layout:
<div id='container'>
<ul><li><span>loading...</span></li></ul>
<button data-category="adjectives">Refresh</button>
<ul><li><span>loading...</span></li></ul>
<button data-category="brands">Refresh</button>
<ul><li><span>loading...</span></li></ul>
<button data-category="clothes">Refresh</button>
</div>
You can react to all the button clicks like this:
document.getElementById('container').addEventListener('click', update);
The update() event handler can determine which button was clicked by checking out the data- attribute on the button. Then, make the AJAX request and place the data into the correct <ul> by finding the closest or the prev() sibling <ul> element.
function update() {
const category = this.dataset.category;
$.post('ajax.php', {category: category}
, function(data) {
$('button').data(category).prev('ul').html(data.output);
}, 'json')
};

Related

How to change add and remove tags with JS in django template

I have a quiz Django app which consists of two parts. One is showing 10 sentences with their audio to remember, one per page, and the second is asking questions for the same set of sentences. First part was set up with js function which creates pagination in my html the following way:
my_template.html
<button id="prev">prev</button>
<button id="next">next</button>
<ul class="list-articles" id="dd">
</ul>
<script>
var my_objects = `{{ my_objects|safe}}:` #list of items from my view function
function paginate(action) {
console.log(action)
if (action ==='next') {
page_number++;
}
else{
page_number--;
}
const audio = document.createElement('audio');
audio.src =`/media/${my_objects[page_number].fields.audio}`;
$('#dd').empty();
$('#dd').append('<li><h1>'+ my_objects[page_number].fields['content'] +'</h1></li>');
$('#dd').append(audio);
$('#page_number').val(page_number);
}
document.addEventListener('DOMContentLoaded', function() {
document.querySelector('#next').onclick = function() {
paginate('next'); #same goes for 'prev' button.
}
})
</script>
Now when the user paginated through all the sentences I want to show the continue button and if the user clicks it, start the second part. The second part has absolutely same page except I need to hide content of my objects and leave only audio or vice versa and add textarea tag for the user's input. After that I need to loop over my objects again - now in the form of questions. I need to do that without page re-rendering so I don't need to store the list of objects or query the DB again.
I tried to make it with tag disabling and activating the second loop after the first ends but that looks quite messy and I suppose there is a smarter way of doing that. I'm not a JS developer so any help would be appreciated!
Thanks for all who viewed and not commented! I found the answer myself. Just need to add JS functions which will empty the main tag and refill with necessary field.
Details: By accessing elements of DOM in the following way you can empty any element on a web page. For example, if we have a div like:
<div class= id="maindiv">
<h2> Hello, world! </h2>
</div>
We can empty and refill it with a new header:
var container = document.getElementById("maindiv");
container.empty();
new_header = document.createElement('h2');
new_header.innerHTML = 'Hello, brave new world!'
container.append(new_header);
Same applies to the whole web-page. The only thing is, if it is too big, you probably going to be tired of manipulating the DOM each time. So, you may want to check out some frameworks like React to make it easier .

How do I create 5 JQuery AJAX calls using eventhandlers?

I am developing a registry system for my school/workplace, and the instructors need a thorough list of the students who fall into 5 separate categories:
Have not met and have not registered absence
Have not met but registered absence after 8 am
Have not met but registered absence before 8 am
Have met but registered reason for absence after 8 am
Have met and registered before 8 am
As the first one (Have not met and not registered) will be loading student data across 3 databases for checkup, getting the data might take some time. I figured instead of loading all the data through PHP, displaying a white screen to the user until everything is loaded, instead I would load the page and then get the data using JQuery AJAX functions.
The AJAX loading and displaying works using this code:
//Not met and not registered
div1 = $("#not-met-not-registered");
div1.find(".text").html("<img src='' class='loader'>");
$.post("/admin_post/getusers", {area:"not-met-not-registered"}, function(data) {
div1.find(".text").html(data);
div1.find("tr").each(function (row) {
datatable1.push({
"Row": $(this),
"Navn": $(this).find("td.navn").html()
});
});
});
However, this only works as I staticly input the div value, and save the div value in 5 different names (div1, div2 etc.).
To receive the data, I have 5 divs looking like this:
<div id="not-met-not-registered" class="list">
<label>Students who have not met and not registered absence</label>
<img src="/images/search.png" class="search">
<input type="text" class="search">
<div class="text"></div>
<input type="button" value="Print">
</div>
Each div has the unique id that AJAX should send via POST to get the liable data. Which is why I figured something along the lines of this would be applicable:
$("div.lists div.list").each(function() {
$(this).on("ready", {div: this}, function (eventObject) {
div = eventObject.data.div;
$.post("/admin_post/getusers", {area: $(div).attr("id")}, function (data) {
div.find("div.text").html(data);
div.find("tr").each(function (row) {
datatable.push({
"Row": $(this),
"Name": $(this).find("td.name").html()
});
});
});
});
});
The function would save the div in question inside the eventObject.data array, and use the id of that div as search criteria on the PHP page. By saving the div as a value in the eventObject, I would be able to use the same name other places I figured, since, as seen below, that idea worked for my search function using eventhandlers.
Each table is given their own search opportunity using a functional eventhandling code, though not yet built for the full purpose:
$(this).find("input[type=text].search").on("change", {preVal: ""}, function (eventObject) {
preVal = eventObject.data.preVal;
curVal = $(this).val();
if (preVal != curVal) {
eventObject.data.preVal = curVal;
alert(curVal);
}
});
I am aware that I am not a very skilled JS or JQuery coder, and perhaps I am going way out of best practice or missing something very obvious. I really hope you can help me out anyway though!
I managed to find out what the fault was, and figure I would post it here.
So, for some reason, when you call a function in JQuery and save a variable in it, the next time you call the same function and save a new value in the variable, the new variable is saved in the old function call.
Right now I save the element e in div
div = e;
When I call it 5 times:
div = 1
div = 2
div = 3
div = 4
div = 5
Then, when the AJAX returns, what it sees is this:
div = 5
div = 5
...
By removing the div part of it, I made it work:
function load_in(e, link, target, data)
{
$.post(link, {data:data}, function(data) {
$(e).find(target).html(data);
enable(e);
setCount(e);
});
}
This function takes the e-lement, the link you AJAX to, the Target that you want your result to go into and whatever data you wish to send as POST data
Callable with this:
load_in(this, "/admin_post/getusers", "div.list", $(this).attr("id"));

Sending an array in a javascript function with append

So here is the situation, im creating a clickable dynamic table by adding row with a button. Each row have informations and can be clicked (the entire row). I look for a way to send the information of the row I clicked to another js function who will copie the row in another dynamic table. But here is the trick : to create a clickable row, I use the function .append and I create the row in a < a> tag which will use href="function_to_add_the_copied_row" to call the other function.
The problem is I cant find out the good syntax. Any suggestion for syntax or other way to do the trick would be appreciated. Here is my code :
//javascript function to make clickable rows
{
var infos = modules_found[i].split("\\t");
rowNum++;
//word ="'Row number : "+infos[0]+"'";
$(".targets").append('<li> <div class="ui-grid-a"><div class="ui-block-a ui-grid-b"><div class="ui-block-a">'+infos[0]+'</div><div class="ui-block-b">'+infos[1]+'</div><div class="ui-block-c">'+infos[2]+'</div></div><div class="ui-block-b ui-grid-b"><div class="ui-block-a">'+infos[3]+'</div><div class="ui-block-b">'+infos[4]+'</div><div class="ui-block-c">'+infos[5]+'</div></div></div></li>');
}
//javascript function who receive the array and add the copied row
function transferArray(infos)
{
alert("in transferArray function");
$(".copied").append('<li> <div class="ui-grid-a"><div class="ui-block-a ui-grid-b"><div class="ui-block-a">'+infos[0]+'</div><div class="ui-block-b">'+infos[1]+'</div><div class="ui-block-c">'+infos[2]+'</div></div><div class="ui-block-b ui-grid-b"><div class="ui-block-a">'+infos[3]+'</div><div class="ui-block-b">'+infos[4]+'</div><div class="ui-block-c">'+infos[5]+'</div></div></div></li>');
}
Here is a high level approach (assuming you know jQuery): instead of wrapping your row inside A tag, better way is to have register a click event listener on your table (via jQuery APIs and not in HTML). In that click handler you can get the index of row clicked easily (make use of jQuery APIs) and once you have the rowindex, you can easily clone the row and move it to somewhere else.
typically how this is handled - if you are not using some type of javascript library like Angular or Knockout is to just store data in the actual HTML with data attributes. you can make as many data attributes as you want as long as they start with data-
ex.
$(".targets").append('<li data-id="xx" data-name="xx" data-custom=""> <a href="...
then I would recommend using jQuery click handler on every row by giving them all a class , ex.
$(".targets").append('<li class="rowClick" data-id="xx" data-name="xx" data-custom=""> <a href="...
then handle the click like this
$(document).on('click' , 'rowClick' , function(e){
var $this = $(this);
//get data of row clicked
var idClicked = $this.attr('data-id');
var nameClicked = $this.attr('data-name');
// you also have the full HTML of the clicked row if you need to copy somewhere
var rowHtml = $(".copied").append($this);
});
You're already using jQuery , so use it to handle the click and then you have the element clicked as a jQuery object right there . You can use native javascript function to handle the click and pass data like you were , but you already are using jQuery and that will automatically bring in a lot more data for you.
Finally, I used native javascript function since the suggested solution didn't work, even if it looks all legit. So here is what I have done:
(...)
//append the js function
$(".FindTable").append('<li id="addedFindRow"><div class="ui-grid-a"><div class="ui-block-a ui-grid-b"><div class="ui-block-a">'+infos[0]+'</div><div class="ui-block-b">'+infos[1]+'</div><div class="ui-block-c">'+infos[2]+'</div></div><div class="ui-block-b ui-grid-b"><div class="ui-block-a">'+infos[3]+'</div><div class="ui-block-b">'+infos[4]+'</div><div class="ui-block-c">'+infos[5]+'</div></div></div></li>');
}
function copyrow(info0,info1,info2,info3,info4,info5)
{
//use data
}
This is quite heavy, but that will do. Would be more chaotic if there was more parameters thought.

How to access dynamic content in list from setInterval

In javascript, On $(document).ready(function() I have a function updateRecordTime() run to update dates in list items (with nested divs), and I have a setInterval() run that function every 30 seconds to update, but after a dynamically add list items via ajax they are not updated also. I have the function running on a static element to look at an Unordered List for its list items but its not working and I can't figure it out.
updateRecordTime = function () {
$('#record-lister > li').each(function (index) {
var record_time = $(this).find('.record-time');
date = moment(new Date(record_time.attr('data-date')));
update = function () {
record_time.html('Record - ' + date.fromNow() + ' <i class="icon-time"></i>');
};
update();
});
}
updateRecordTime();
setInterval(updaterecordTime, 30000);
The html (relevant code) is as follows
<ul id="record-lister">
<li>
<div class="record-time" data-date="'.date('Y-m-d H:i:s', $timestamp).'">
gets replaced with moment time....
</div>
</li>
<li>
<div class="record-time" data-date="'.date('Y-m-d H:i:s', $timestamp).'">
gets replaced with moment time....
</div>
</li>
</ul>
If I call the function after a successful ajax call, it's updated once, but the loop doesn't find it, I'm aware setInterval() will loop the original list items but how can I get it to also find the dynamically added ones too?
As A.Wolf said, your code should find the newly inserted li tags.
Make sure you correctly populate the ul tag having id="record-lister". You can inspect the html code added dynamically to make sure of this.
Watch js errors from your browser console so that the code doesn't break at some point. I recommend you Firebug or Chrome Dev Tools.
When calling setInterval(funcName, interval), you must be aware that funcName is a pointer to your function and you must pay attention to function scopes. Calling setInterval("funcName()", interval) will call funcName from the global scope ( i.e. window.funcName ).
Based on the code given by you here, those are my advices, and it should work just fine assuming that adding html code via ajax works good.

How to edit list item on another page

The Situation
In Sharepoint 2010 I can click on an item in a list:
And then the Read/Edit view becomes visible in that page:
My Goal
I have a WebPart on another Page where I show some items coming from this and several other lists and I want to add a read or edit link to each of them.
How can I do that?
I'm searching for a function like EditListItem('ItemId', 'ListId', ...) which will open the edit div window.
What have I tried
The a tag generated by Sharepoint on "Test Item" above is like this:
<a onfocus="OnLink(this)"
href="http://{mysharepointsite}/_layouts/listform.aspx
?PageType=4
&ListId={D0FDB54F-1DDF-4C5E-865B-ABDE55C1125}
&ID=1
&ContentTypeID=0x010800ED5176D13CCEFC4AA8D62A79985DE892"
onclick="EditLink2(this,49);return false;" target="_self">Test Item</a>
So I digged a bit into the Sharepoint JS files and found EditLink2 calling _EditLink2 which calls ShowPopup from the context (the 49) is the context no and seems to be dynamic.
I tried to fake the context but there are billions of variables and I think I can't get that to work stable.
On that page where you need to open dialog just write simple JS function for showing modal dialog, for example:
function openMyItemDialog( itemId ) {
var options = {
url: "http://{mysharepointsite}/_layouts/listform.aspx?PageType=4&ListId={D0FDB54F-1DDF-4C5E-865B-ABDE55C1125}&ID=" + itemId + "&ContentTypeID=0x010800ED5176D13CCEFC4AA8D62A79985DE892&IsDlg=1",
width: 500,
height: 500,
title: "Item view/edit"
};
SP.UI.ModalDialog.showModalDialog( options );
}
Note the &IsDlg=1 param at url
And then modify href link where you display your items.
For example:
Test item
Replace 35 to ID of your item
I know this is an old question, but there's another way to achieve what the OP was trying to do.
In a XSLT ViewWebPart, there is a global parameter named $ViewCounter.
This is the context number required by the _EditLink2 function.
So, in order to add a link to the display form and have it opened in a dialog, wrap the item in an <a> tag like this:
your item
Notice the variables $HttpVDir, $List, $thisNode/#ID and $ViewCounter (no need to hard-code any value).

Categories