How do I create 5 JQuery AJAX calls using eventhandlers? - javascript

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"));

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 .

Appending to div puts first item at bottom, and rest in correct order

I am trying to add items into a div by appending to the end of the div during a for loop. The idea being I have records from a database with a "Total score" field, and I want the highest-scoring records on top.
The container div where I want to put the elements is this:
<div id="fetch-n-sites-output"></div>
And my callback function upon a successful AJAX call is as follows:
function updateNSites(data){
$("#fetch-n-sites-output").empty();
data["fetched-sites"].map(function(element, index){
var cloned = cloneTemplate(index, element);
console.log(`Processing site ${index}...`);
$("#fetch-n-sites-output").append(cloned);
});
}
For completeness, my cloneTemplate function is this:
function cloneTemplate(index, data){
// CLONE TEMPLATE
var template = $("#test-template").html().trim();
var clone = $(template);
var siteID = data["site_id"];
var totalSiteScore = data["total_site_score"];
// UPDATE CLONE WITH SITE-SPECIFIC INFORMATION
// 0. Update ID of div element with site id
var mainDiv = $("div.greendiv").eq(index);
mainDiv.attr("id", `site-${siteID}`);
// 1. Update header with "Site ID: {site_id}"
$(`#site-${siteID} > div.reddiv > p.header-text`).text(`Site ID: ${siteID} - Total Score: ${totalSiteScore}`);
// 2. Add event handlers
// $(`#site-${siteID} div.site-sidenav`).on("click", 'a', function() {
// alert("clicky");
// });
$(`#site-${siteID}`).find(".site-navlink").on("click", function () {
alert("clicky");
});
// SHOW CLONE
clone.removeClass("template-hidden").addClass("template-show");
return clone;
}
When I retrieve 3 sites, the highest scoring site is always at the bottom. This doesn't make sense to me, because my SQL query orders by this total site score. The highest scoring site is always placed at the bottom, but the other sites are ordered properly:
Another odd problem (which I think is related to this main issue) is that the event handlers that I add in the cloneTemplate function to handle clicks on the <a> tags (right now just calls an alert() for testing) only work on the elements other than the first:
Whereas clicking 'GENERAL' on the first element does not trigger an alert. Could someone please help me figure out why every added element but the first behaves properly, but the first does not? And why the first element is always getting placed at the bottom? Thank you

Options to make jQuery Ajax code more efficient / logical

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')
};

How to add HTML code to a different HTML file using JavaScript or PHP

Alrighty, so I am trying to make a little page on my website that takes a few values and then when you click a button, it adds those values inside of a div on a different HTML page.
My code is:
<input type="text" name="URL"><br>
<input type="text" name="ImageURL"><br>
<input type="text" name="Title">
<button onclick="addCode()">Submit</button>
So for the addCode() function I want it so that it adds the values inside of a the item div on a different HTML file just like:
<div class="item">
<div class="animate-box">
<a href=URL><img src=ImageURL></a>
<div class="fh5co-desc"><a style="TEXT-DECORATION:none; COLOR:#818892; LINE-HEIGHT:20px;" href=URL>Title</a></div>
</div>
</div>
Thanks in advance.
What you are doing is technically impossible. without some sort of persistence, that is;
you cannot edit a page you aren't on. web browsing is a stateless technology.
if you meant you want to fill out those inputs then redirect on click and have those values available, there are a few different ways to do it:
1) Query String
write your code on the second page in a way that it accepts params from a query string in the url bar
function getURLParameter(name) {
return decodeURIComponent((new RegExp('[?|&]' + name + '=' + '([^&;]+?)(&|#|;|$)').exec(location.search) || [null, ''])[1].replace(/\+/g, '%20')) || null;
}
var textDecoration = getUrlParameter('textdec'),
color = getUrlParameter('color'),
lineHeight = getUrlParameter('lnheight');
then you can send the request for the page as
http://page.com/page?textdec="someval"&color="somecolor"&lnheight="someheight"
however this will not work if you are not going directly to that page after your current one
2) localStorage
on your first page set the local storage values:
localStorage.setItem('lineHeight', 'someVal');
localStorage.setItem('color', 'someColor');
localStorage.setItem('textDecoration', 'someVal');
then on your second page retrieve the values
var lineHeight = localStorage.getItem('lineHeight'),
color = localStorage.getItem('color'),
textDecoration = localStorage.getItem('textDecoration');
3) serverSide persistence
this will vary MASSIVELY depending on how you your backend is structured
but the general gist is make a post request (ajax or otherwise) &
collect the data on the backend
then when you render the second page send the variables that were posted, either through interpolation or included as script variables
The only way to do this (without getting other technologies involved) is to use the localStorage, storage event. And, even with this, it will only work when the two pages are coming from the same domain and are open in different browser tabs (of the same browser) at the same time.
If those conditions are present, then modifying localStorage on one page will fire the storage event, which the other page can be set up to listen for. The other page can then respond to the event by pulling new values (that the first page wrote into localStorage) out and placing them anywhere on the second page that you like.
This is the kind of solution that you might encounter if you were on a travel site with more than one browser tab open. You may be looking at different flight options in different tabs. If one tab's code has an update that any/all other open tabs should know about, this technique does the trick.
Here's an example of how to set values into localStorage and use them. But, localStorage doesn't work here in the Stack Overflow snippet environment, so you can run the code here.
Once the values are in localStorage, you can pick them up from any other page that is being served from the same domain. So, the "getItem" code I'm showing here would really be placed on your "page2.html".
// Get DOM references:
var name = document.getElementById("name");
var color = document.getElementById("color");
var airspeed = document.getElementById("airspeed");
var btn = document.getElementById("btnGo");
// Set up button click event handler:
btn.addEventListener("click", function(){
// Get values and place in localStorage
localStorage.setItem("name", name.value);
localStorage.setItem("color", color.value);
localStorage.setItem("airspeed", airspeed.value);
// For demonstration, get values out of localStorage
console.log("What is your name? ", localStorage.getItem("name"));
console.log("What is your favorite color? ", localStorage.getItem("color"));
console.log("What is the airspeed of a laiden swallow? ", localStorage.getItem("airspeed"));
// If you wanted to redirect the user to the second page, now that the intial values
// have been set, you could just do:
location.href = "path to second page";
});
<div>What is your name?<input type="text" id="name"></div>
<div>What is your favorite color?<input type="text" id="color"></div>
<div>What is the airspeed of a laiden swallow?<input type="text" id="airspeed"></div>
<button id="btnGo">Go!</button>
If you're trying to edit the actual source code of the file, you'll need something like PHP. Otherwise, JS is just fine.
PHP Solution
You could use something like this:
<?php
$old = file_get_contents("some_page.html");
$content = explode("<span>",$old,2); // replace <span> w/ opening tag
$content = explode("</span>",$content[1],2); // replace </span> w/ closing tag
$data = "new content of element";
$new = str_replace($content[0],$data,$old);
?>
Updated JS Solution
You can't use my previous solution. Instead, you would have to create a function in the second HTML file that could be called from the first file, like this:
A script in file2.html:
function set(id,val){
$("#"+id).html(val); // jQuery
document.getElementById(id).innerHTML = val; // pure JS
}
A script in file1.html:
var win = window.open("http://example.com"); // open the window
win.set("some_id","Some content.") // the function that we set earlier
Note that this is reverted once the user closes or reloads the tab, and only applies to that user and that tab.

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.

Categories