Submitting form/getting HTML with JavaScript without iframe? - javascript

Context:
I work a student job transcribing paper reports in a webapp. It's old and we unfortunately can't change the source nor directly run a DB query.
It only checks if the unique ID exists once you submit the entire form, and you can't submit it unless it's entirely filled. Needless to say, it's a huge waste of time as you often transcribe the whole thing only to realise it's a duplicate.
Objective:
I made the userscript below that launches a search the search on the onblur of the unique ID's input(noReferenceDeclarant), checks if there are any matches (rows) and returns accordingly. Runs with Greasemonkey. The search form is in another page on the same domain. The search form does not take any URL arguments.
Can this be done without using an iframe (AJAX perhaps?)
This is a tool for my own productivity & to learn JS at the same time. As I'm still very much a beginner, any tips to make that code cleaner are welcome.
//Adding function to input's blur event
$(document).on ("blur", "#noReferenceDeclarant", isRefNumberExists);
//Vars
var noReferenceDeclarant = '';
var loadCode = 0;
var $searchForm;
//Fonctions
function isRefNumberExists ()
{
noReferenceDeclarant = $('#noReferenceDeclarant').val();
loadCode = 0;
//Make sure there's data in the input before proceeding
if (noReferenceDeclarant)
{
//Build search iframe
$searchForm = $('<iframe />', {
name: 'searchWindow',
src: 'rechercherGriIntranet.do?methode=presenterRechercher',
id: 'searchWindow',
width: 0,
height: 0
}).appendTo('body');
$searchForm.load(searchRefNumber);
}
}
function searchRefNumber()
{
var isExists = false;
//Check which "load" it is to avoid submit loops
if (loadCode === 0)
{
loadCode = 1;
//Filling search form with search term
$(this.contentDocument).find('#noReference').val(noReferenceDeclarant);
//Set search form preferences
$(this.contentDocument).find('#typeRapportAss').prop('checked', false);
$(this.contentDocument).find('#typeRapportAS').prop('checked', false);
$(this.contentDocument).find('#typeRapportSI').prop('checked', true);
//Submit the form
$(this.contentDocument).find('form:first').submit();
}
else if (loadCode === 1)
{
loadCode = 2;
//See if there are any tr in the result table. If there are no results, there a thead but no tr.
var foundReports = $(this.contentDocument).find('.resultatRecherche tr').length;
if (foundReports > 0)
{
if (confirm('A report matching this ID already exists. Do you want to display it?'))
{
//Modal window loading the report in an iframe. Not done yet but that's fairly straightforward.
}
else
{
//Close and return to the form.
}
}
}
//Reset variables/clean ressources
delete $searchForm;
$('#dateRedactionRapport').focus();
}

On the whole I've seen far, far worse code.
Ajax could do it, but then you'd just have to put the AJAX response into the DOM (as an iframe, most likely).
In this instance, I'd keep the approach you have. I think it is the sanest.j
Without the full context, there may be a way to clean up the loadCode -- but what you have is pretty same and works. A lot of folks would call it a semaphore, but that is just an issue of terminology.
The only thing I"d really clean up is recommend not calling the jQuery object so often..
// Many folks recommend that jQuery variables be named $<something>
var $doc = $(this.contentDocument);
doc.find('#typeRapportAss').prop('checked', false);
$doc.find('#typeRapportAS').prop('checked', false);
$doc.find('#typeRapportSI').prop('checked', true);
If you wanted to play with jQuery data structures, you could make a 'config' object that looks like this:
var formValues = {
typeRapportAs: false,
typeRapportAS: false,
typeRapportSI: true
};
then iterate over that to (using for ... in with .hasOwnProperty).
Not NEEDED for this project, what you are doing is fine, but it might make a learning exercise.

Related

What is W3.JS and why is W3Schools promoting it?

I was noticing that W3Schools has a tutorial on what they're calling W3.JS, which in my opinion appears to compete with jQuery. I was having trouble finding information on it from any source other than w3Schools themselves.
W3Schools does appear (in my opinion) to have some propensity to promote semi-abandonware. For example, AppML.
Is this library actually used anywhere? What is its history (especially its release history) and if this is actually going to be developed into something that would actually be worth considering using?
Note: I'm not seeking a library recommendation here - I do realize that that's explicitly off-topic - I'm just asking for background information on this particular library.
The W3 JS library has a couple of attractive properties.
1. It's very lite.
2. There's not much of a learning curve; I was able to get a few things "working" with little to no documentation (and beyond w3schools.com, there's not much.)
I tried several different pieces of functionality in the library and here's my take on it.
To try and gain some separation of concerns, I stumbled on this library looking for the ability to use a PHP-like include statement to pull in an HTML fragment file. W3.JS provides the ability to add an attribute to a div element
<div w3-include-html="frags/content.html"></div>
And with a JQuery UI-esque initialize statement, the file's contents would be loaded into your div element on the client side.
<script>
w3.includeHTML();
</script>
This does work but it's about all that does. I used the library on a legacy App I inherited that was done in Classic ASP on the server side. One thing I relied upon from JQuery was the AJAX function for POST requests to a crude ASP page I used as a "data access layer." The page would use a POST variable like an enum to tell it which function to call and then subsequent variables as needed in each function. Below is an example of W3.JSs ability to perform a Web Request and then use it's display-object function with an HTML object (here an un-ordered list) to displayed a retrieved JSON object that has an attribute "customers" that is an array. This will generate LI elements with an innerHTML that is an attribute of each customer instance; "CustomerName." ({{CustomerName}}) is a place holder here.
<ul id="id01">
<li w3-repeat="customers">{{CustomerName}}</li>
</ul>
<script>
w3.getHttpObject("CommonServerFns.asp?operation=1", controllerFn);
function controllerFn(myObject) {
//myObject looks like {"customers":[{"CustomerName":"Mike Jones"}]}
w3.displayObject("id01", myObject);
}
</script>
So to even try the above example I had to convert my data page to use the Query String of a GET request, which I didn't have a problem with. However, used in Lieu of a JQuery AJAX request and a vanilla JS callback function, like below.
$.ajax({
url: "ServerCommonFns.asp",
type: "POST",
data: '{"operation":1}',
complete: function(jqXHR, statusMsg){
//if status is 200, jqXHR.responseText has my serialized JSON
var retData = JSON.parse(jqXHR.responseText);
var tgtListElement = document.getElementById("id01");
//Clear the placeholder code from the list and go to work...
tgtListElement.innerHTML = "";
for(var index = 0;index<retData.length;index++){
var listNode = document.createElement("li");
listNode.innerHTML = retData[index].CustomerName;
tgtListElement.appendChild(listNode);
}
});
So W3JS looks like the preferred solution, it's less code for sure. However, in the app I worked on, I timed the web requests for both method and the results were shocking. The request returned a JSON string that was roughly 737K in size and the request took ~2.5 seconds to complete. My JQuery solution took about ~4.5 seconds including the ~2.5 second request. The W3.JS solution took anywhere from 17 - 27 seconds to complete. I did some tweaking thinking that I had made a mistake and that was the only way the "Better & Faster Javascript Library" could be running that poorly but it appears not. I don't know why but the W3 JS solution was clearly not usable in my app.
I tried to use this library again early last week; thinking an application less data intensive might work much better. If you've read this far I'll save you a few more lines of reading....it wasn't. This app used Classic ASP with another "data access layer" type ASP page, again utilized with JQuery and Vanilla JS using the AJAX function. I built a JS function to pass as param/function ptr like so
var sortAsc = true;
//JSONObj looks like {"faqs":[{id:1,createdBy:"Les Grossman"},
//{id:2,createdBy:"Jeff Portnoy"}]};
var JSONObj = null;
function compareFn(a, b){
if(a.id < b.id) return sortAsc ? 1 : -1;
if(a.id > b.id) return sortAsc ? -1 : 1;
return 0;
}
//Assign my onclick event handler
document.getElementById("faqId").onclick = function(){
JSONObj.sort(compareFn);
sortAsync = !sortAsync;
//Logic below to clear and reload the rows in my HTML table...
//See above example for pretty much identical code
}
This again is fairly code intensive and I didn't want to have to fire of a web request everytime a column header was clicked (to use the sort ability of a SQLite Query) rather than a custom JS function. So I tried using the w3.sortHTML() function to do the job. It looked something like this but I kept my JQuery AJAX code to load the table initially and used the sortHTML() function post load for sorting. The w3.sortHTML function takes params in this order
Repeating element container unique selector ("Select, UL, OL, Table, etc.").
Repeating element group selector (option, li, tr, etc.) In this instance I use .faq as I've applied it to all table -> tr instances.
Selector for attribute/element of repeating group to sort by (Keeps track of ascending/descending sort order internally.)
FAQ Number
FAQ Name
1
Tug Speedman
2
Alpa Chino
3
Kirk Lazarus
...
I'm using this page to display FAQ Data from our HelpDesk system (uses my login to scrape data into a SQLite database so any user can see FAQ data without needing a login/license for a Helpdesk system.) The web request returns a JSON string roughly 430 KB in size and creates 320 table rows with 5 table columns (for our e.g. only two columns are coded for.) This request takes ~3 seconds to complete. My JQuery/JS AJAX request w/ callback function roughly ~5.5 seconds total, including the ~3 secs for web request/response. My custom sort fn took less than 1 second, regardless of sort by column (only id shown in example), to sort my ret data obj (sorted as a global object "JSONObj"), clear the table and refill the table with sorted rows. Again, lots of code to do this so I thought W3 JS might help me out. My W3 JS sort implementation takes ~8 seconds to sort integer/numeric data and ~10 seconds to sort text/string data. This is way too long for my needs (again) so I reverted back to my JQuery/JS solution.
In conclusion, it seems that anything of greater than trivial size begets really poor performance in this library. I think the creators of W3.JS had their functional requirements correct as the library does boast some very useful abstracted functionality but it's no replacement for JQuery and other existing frameworks or even your good old fashioned Javascript code. Wish things had worked out; would have been really helpful.
EDIT -
So I've been going through the source of the library and, while there are several shorthand methods for web reqs, there is simply a w3.http() method with the following signature.
function (target, readyfunc, xml, method)
The params are appropriately named except for xml which is just a string value. I was able to get this working with my previously mentioned ASP page that handles POST requests. I invoked it like so.
w3.http("ServerCommonFns.asp", httpCallBack, "operation=1", "post");
function httpCallBack(){
if(this.readyState == 1){
console.log("Setting needed header...");
this.setRequestHeader("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8");
}
else if(this.readyState == 4){
console.log("Parsing response....");
//document.getElementById("main").innerHTML = JSON.stringify(this.responseText);
var responseObj = JSON.parse(this.responseText);
var respDataObj = JSON.parse(responseObj.retdata);
var tableObj = document.getElementById("responseTable");
tableObj.innerHTML = "";
for(var index=0;index<respDataObj.length;index++){
var newRow = document.createElement("tr");
newRow.innerHTML = "<td>" + respDataObj[index].MACHINE_ID + "</td>";
tableObj.appendChild(newRow);
}
return false;
}
}
You'll see httpCallBack is used like it's the listener for the onreadystatechange event for the internal xhr object because it is just that. In order to work in my instance, I had to set the content-type header before the request was opened so my xml/param argument was interpreted properly. So W3.JS can do POST requests but the w3.http() function is little more than a simple wrapper around the Javascript XMLHttpRequest() object. Also, the requests are invoked Asynchronously and there's no way to change that behavior so just FYI.
--SECOND EDIT. So I've got a lull in work and I felt I may have given W3.JS less than it's due. Today I experimented with a few things and examined the source for a little insight. I found a couple things that I find at least neat and I thought I'd finish this wall of text with them. Below this is the source for what I did today. I tried combining a few W3.JS functions with an existing data source for an app I wrote for our MFG Q/A folks.
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Test Page</title>
<script src="https://www.w3schools.com/lib/w3.js"></script>
<script>
var xhrObj = null;
var jsonObj = null;
var sortAsc = true;
function w3ReadyStateChanged(){
if(this.readyState == 1){
console.log("Preparing Request/Setting needed header...");
this.setRequestHeader("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8");
}
else if(this.readyState == 4){
console.log("Parsing response....");
//document.getElementById("main").innerHTML = JSON.stringify(this.responseText);
var responseObj = JSON.parse(this.responseText);
if(responseObj.retcode != 1){
console.log("A retcode of: " + responseObj.retcode + " has indicated a failed web request...");
return false;
}
jsonObj = {};
//var respDataObj = JSON.parse(responseObj.retdata);
jsonObj.retdata = JSON.parse(responseObj.retdata);
//
console.log("Starting Display: " + new Date())
w3.displayObject("responseTable", jsonObj);
console.log("Display Finished: " + new Date())
//This is to stop page refresh.
return false;
}
}
function tryw3xhr(){
w3.http("ParCommonFns.asp", w3ReadyStateChanged, "operation=13&useCriteria=false&openPARs=1", "POST");
}
function compareFn(a, b){
if(parseInt(a[0]) > parseInt(b[0]))
return 1;
else if(parseInt(a[0]) < parseInt(b[0]))
return -1;
else
return 0;
}
function sortContainerData(containerElementId, repeatingSelector, sortByIndex){
//w3.sortHTML('#responseTable','tr.dataRow')
var dataObj = {};
var containerElement = document.getElementById(containerElementId);
if(!containerElement){
console.log("Couldn't locate a table or list with ID: " + containerElementId);
return false;
}
//
var sortElements = containerElement.querySelectorAll(repeatingSelector);
if(!sortElements || sortElements.length == 0){
console.log("repeatingSelector failed to yield results: ");
return false;
}
//
dataObj.sortElements = new Array(sortElements.length);
for(var i = 0;i<sortElements.length;i++){
var tempArray = new Array(sortElements[i].children.length);
for(var j = 0;j<sortElements[i].children.length;j++){
tempArray[j] = sortElements[i].children[j].innerHTML;
}
dataObj.sortElements[i] = tempArray;
}
//w3.sortHTML('#responseTable', '.dataRow', 'td')
console.log("Starting Sort: " + new Date());
var t0 = performance.now();
var doCustom = false, didSwap = false;
if(doCustom){
var sortLen = dataObj.sortElements.length;
var j = 0;
//if (parseInt(dataObj.sortElements[i][sortByIndex]) == dataObj.sortElements[i][sortByIndex])
// compareInt = true;
for (var i = 0; i < sortLen - 1; i++){
didSwap = false;
j = i + 1;
while (j < sortLen && parseInt(dataObj.sortElements[i][sortByIndex]) >= parseInt(dataObj.sortElements[j][sortByIndex])) {
j++;
}
//If j equals sortLen, then i is greater than all others so we stick it on top.....
if (i + 1 == j)
break;
if (j == sortLen) {
dataObj.sortElements.push(dataObj.sortElements[i].slice());
dataObj.sortElements.splice(i, 1);
didSwap = true;
} else if (j > (i + 1)) {
dataObj.sortElements.splice(j, 0, dataObj.sortElements[i].slice());
dataObj.sortElements.splice(i, 1);
didSwap = true;
}
if (didSwap)
i--;
//if(i % 50 == 0)
// console.log("Handled: " + i);
}
//This is cheating but it should work.....
if (!sortAsc) dataObj.sortElements.reverse();
}
else{
dataObj.sortElements.sort(compareFn);
}
sortAsc = !sortAsc;
console.log("Sort Time (MS): " + (performance.now() - t0));
//
console.log("Starting Reload: " + new Date()) ;
var containerBody = containerElement.querySelector("tbody");
containerBody.innerHTML = "";
for(var i = 0;i<dataObj.sortElements.length ;i++){
var newRow = document.createElement("tr");
newRow.classList.add("dataRow");
//
for(var j =0;j<dataObj.sortElements[i].length;j++){
var newCol = document.createElement("td");
newCol.innerHTML = dataObj.sortElements[i][j];
newRow.appendChild(newCol);
}
//
containerBody.appendChild(newRow);
}
console.log("Ops complete: " + new Date()) ;
return false;
}
window.onload = function () {
document.getElementById("invokeBtn").disabled = true;
tryw3xhr();
document.getElementById("invokeBtn").disabled = false;
w3.hide("#conditionalContent");
};
//
function runW3JSFn() {
var w3TargetId = "#w3Target";
var w3FunctionsSelect = document.getElementById("w3Functions");
if (w3FunctionsSelect.value == "show") {
w3.show(w3TargetId);
}
else if (w3FunctionsSelect.value == "hide") {
//Doesn't preserve space....
w3.hide(w3TargetId);
}
else if (w3FunctionsSelect.value == "toggle") {
w3.toggleShow(w3TargetId);
}
else if (w3FunctionsSelect.value == "addStyle") {
//But no remove style?
w3.addStyle(w3TargetId, 'border', '2px solid green');
}
else if (w3FunctionsSelect.value == "addClass") {
w3.addClass(w3TargetId, 'w3Class');
}
else if (w3FunctionsSelect.value == "removeClass") {
//Italics should go away.....
w3.removeClass(w3TargetId, 'w3Class');
}
else if (w3FunctionsSelect.value == "toggleClass") {
//Italics should go away.....
w3.toggleClass(w3TargetId, 'w3Class');
}
else if (w3FunctionsSelect.value == "filterTable") {
//Italics should go away.....
document.querySelector(w3TargetId).innerHTML = "<h2> Try an ID # in the box below....</h2>";
}
else { document.querySelector(w3TargetId).innerHTML = "<h2> Invalid function specified....</h2>"; }
}
//
function doVanillaJSFn() {
var w3TargetId = "#w3Target";
var w3FunctionsSelect = document.getElementById("w3Functions");
if (w3FunctionsSelect.value == "show") {
document.querySelector(w3TargetId).style.display = 'block';
}
else if (w3FunctionsSelect.value == "hide") {
//Doesn't preserve space....
document.querySelector(w3TargetId).style.display = 'none';
}
else if (w3FunctionsSelect.value == "toggle") {
var tgtElement = document.querySelector(w3TargetId);
if (tgtElement.style.display == 'block')
tgtElement.style.display = 'none';
else
tgtElement.style.display = 'block';
}
else if (w3FunctionsSelect.value == "addStyle") {
//$(tgtElement).css("attr", "val");
//Works....
document.querySelector(w3TargetId).setAttribute("style", "border: 4px solid green");
//But better.....
if(confirm("Try Better way ?"))
document.querySelector(w3TargetId).border = "4px solid green";
}
else if (w3FunctionsSelect.value == "addClass") {
document.querySelector(w3TargetId).classList.add("w3Class");
}
else if (w3FunctionsSelect.value == "removeClass") {
//Italics should go away.....
document.querySelector(w3TargetId).classList.remove("w3Class");
}
else if (w3FunctionsSelect.value == "toggleClass") {
//Italics should go away.....
var tgtElement = document.querySelector(w3TargetId);
if (tgtElement.classList.contains("w3Class"))
tgtElement.classList.remove("w3Class");
else
tgtElement.classList.add("w3Class");
}
else if (w3FunctionsSelect.value == "filterTable") {
//Italics should go away.....
document.querySelector("#filterCtrl").oninput = function () { myCustomFilter() };
document.querySelector(w3TargetId).innerHTML = "<h2> Try it now....</h2>";
}
else { document.querySelector(w3TargetId).innerHTML = "<h2> Invalid function specified....</h2>"; }
}
function myCustomFilter() {
var tableElement = document.getElementById("responseTable");
var filterData = document.getElementById("filterCtrl").value.trim().toLowerCase();
////
for (var i = 1; i < tableElement.rows.length; i++) {
var foundRowMatch = false;
if (filterData == "") {
tableElement.rows[i].style.display = 'table-row';
continue;
}
for (var j = 0; j < tableElement.rows[i].cells.length; j++) {
var cellSplit = tableElement.rows[i].cells[j].innerHTML.trim().split(' ');
for (var k = 0; k < cellSplit.length; k++) {
if (cellSplit[k].trim().toLowerCase() == filterData) {
foundRowMatch = true;
break;
}
}
if (foundRowMatch) break;
}
// //
if (!foundRowMatch) tableElement.rows[i].style.display = 'none';
else tableElement.rows[i].style.display = 'table-row';
}
//
return false;
}
</script>
<style>
#parHeaders{
background-color: red;
border-bottom: 4px solid black;
}
.w3Class {
font-style:italic;
}
</style>
</head>
<body>
<select id="w3Functions">
<option value="show">Show</option>
<option value="hide">Hide</option>
<option value="toggle">Toggle</option>
<option value="addStyle">Add Style</option>
<option value="addClass">Add Class</option>
<option value="removeClass">Remove Class</option>
<option value="toggleClass">Toggle Class</option>
<option value="filterTable">Filter Table</option>
</select>
<button id="invokeBtn" onclick="runW3JSFn(); return false;">Try w3 function</button>
<button id="invokeBtnAlternate" onclick="doVanillaJSFn(); return false;">Try JS Alternate Function</button>
<div id="w3Target"><h2>This Is My W3 Functions Target!!!!</h2></div>
<br/><br/>
Filter Data By Id: <input id="filterCtrl" type="text" oninput="w3.filterHTML('#responseTable', '.dataRow', this.value);" />
<br/>
<div><table id="responseTable">
<thead>
<tr id="parHeaders">
<th id="parIdHdr" onclick="sortContainerData('responseTable', 'tr.dataRow', 0);return false;">ID</th>
<th id="parTypeHdr">TYPE</th>
<th id="dateSubmittedHdr">SUBMITTED DATE</th>
<th id="priorityLevelHdr">PRIORITY LEVEL</th>
<th id="issueDescHdr">ISSUE DESC</th>
</tr>
</thead>
<tbody>
<tr class = "dataRow" w3-repeat="retdata">
<td>{{PAR_ID}}</td>
<td>{{PAR_TYPE}}</td>
<td>{{DATE_SUBMITTED}}</td>
<td>{{PRIORITY_LEVEL}}</td>
<td>{{ISSUE_DESC}}</td>
</tr>
</tbody>
</table></div>
<div id="conditionalContent"><h2>Loading.......</h2></div>
</body>
</html>
So what I was trying to do was use w3.http() to get a JSON string from my unmodified data source (classic ASP page still) and then use the w3.displayObject() with my retrieved JSON object to populate a table with rows. I tried a similar scenario previously and was met with very poor performance but in the source I examined, I didn't see any obvious bottlenecks so I thought I'd try again. My web request takes ~9 seconds to query 750K worth of data. I used this object to retest w3.displayObject and was surprised. Using the above placeholders, which are simply
{{attribute names}} of my parsed JSON object, I was able to load 677 rows with 5 columns per row in just a few hundred milliseconds. So for a simple grab and go type operation, I would rate this as acceptable. So then I tried to use the w3.sortHTML() function to sort the rows by the first column, an integer value. This was met with problems when I got it working, it took ~30 seconds to work and it froze my browser a couple times. And then when it did finally print the "sorted" rows, they weren't sorted by integer or numerical value but rather by strings so a value of "100" was followed by the value "11" because the first char matches but for the second, the "1" alphachar is higher than "0". I did a little more tooling and I re-read the source to confirm no-joy; as this sort functionality would be a big help to me. I found out that while W3.JS support sorting lists as well as tables, it does a modified version of a bubble sort and does so by deleting and inserting rows/items in the DOM, not in memory. So I'm going to confirm my prior assertion that this functionality is not a practical sorting implementation.
I mentioned above that JS Array objects have a sort function you can provide a +, 0, or - int value to. To compare, I wrote my own function to sort a table. It works by sorting an array of arrays (each table row column "td"'s innerHTML being put in an Array and then pushed to a containing Array. Then I wrote a modified bubble sort on the first column (my ID column.) I ran into a few problems and it took me 2+ hours to get right. Firstly, it took me 3 - 4 tries to correctly deep copy an array. This is what I thought should do the trick but it would seem not.
var tempArray = new Array(dataObj.sortElements[i]);
What did work was this:
var tempArray = dataObj.sortElements[i].slice();
Then I cut away the array I was moving using this:
dataObj.sortElements.splice(i, 1);
Finally, I used splice again to insert the array in it's proper place while not cleaving any indexes. I also used push to tack it on the end if it encountered no greater compare value. My sort function worked when sorting values in an Ascending direction but, going off sheer memory from my data structures course, I couldn't remember everything I needed to quickly modify for a search in a Descending direction. So I cheated and just sorted Asc and then used array.reverse() to get what I needed. I timed my custom function and worst case it took 56 milliseconds to sort and best case it took .05 milliseconds (the closer to sorted the table is, the better it does.) Then I tried the array.sort() function for comparison.
function compareFn(a, b){
if(parseInt(a[0]) > parseInt(b[0]))
return 1;
else if(parseInt(a[0]) < parseInt(b[0]))
return -1;
else
return 0;
}
//Invoked like so.....
dataObj.sortElements.sort(compareFn);
Worst case the built in function took 4.5 MS and worst case 2.5. So, if you can't sort the data using your database engine, save yourself the time and trouble and just use array.sort() for consistent results on anything other than data of an overwhelming volume.
As far as the utility functions of W3.JS, I implemented Vanilla JS solutions in an adjacent function and their util functions seemed to work as advertised, though some seemed repetitious (e.g. addClass, removeClass, toggleClass. Just have a param for toggleClass?)
So w3.http, with a little customization, seems to work adequately and w3.displayObject is also worth checking out. The last thing that caught my attention was the w3.filterHTML() function with the following signature.
w3.filterHTML('#responseTable', '.dataRow', this.value)
It's the unique selector of your table/list, the selector of the tr or ul element and the value to filter against (the value of a text input in this case.) After some tinkering, I was able to filter the data that I retrieved using w3.http and displayed using w3.displayObject(). This function iterates through all rows and all cells of rows to see if the match value is contained in the text of the field. No way to force a numeric comparison but the response was snappy with no discernable lag. So for my actual final conclusion, this library does provide some functionality that's at least worth benchmarking for yourself and it is very light but it isn't going to liberate you from JQuery. That said, you can see in my example that most things W3.JS can do can also be down in good ol' fashioned Vanilla JS. JQuery has been a dependency for just about every web application for the past decade and change but what do you do if you inherit a web app that references an outdated version of JQuery that you can't simply replace? This happened to me on my most recent web application project and I had to learn how to do most JQuery functions with regular JavaScript. It really wasn't that hard, it liberated me from yet another external dependency and, dare I say it, some of it was kinda fun. So try it yourself. Even if you don't use it, you may discover something helpful to archive for later use. Cheers!
W3.JS, which appears to be at least a partial attempt to compete with jQuery.
It does cover the same basic areas as jQuery. i.e. it is a generic library of DOM helper functions with added Ajax.
I was having trouble even finding information on it from anyone other than them.
That is because it is their library (taking their approach of "Slap W3 at the start of the name and hope people associate it with the W3C" to extremes) and practically nobody else is bothering with it.
W3Schools does appear to have at least some propensity to promote semi-abandonware as the next great thing. Is that what this is?
It does appear so, but that's speculating on the future.
Is this library actually used anywhere?
Nowhere major. Some people who are stumbling across W3Schools and making their first steps on the authoring for the WWW by learning from them are using it. Questions about it crop up on Stackoverflow from time to time.
Does anyone know its history (especially its release history)
The download page has some information.
It includes the rather unhelpful statement W3.JS is free to use. No license is necessary (which is exceptionally vague; free software licenses exist for a reason).
It also has a change log at the bottom.
There is no sign of a version control repository anywhere.
and if this is actually going to be developed into something that would actually be worth considering using?
Opinion, so no comment.
w3.js = tiny, powerful, intuitive
Looks like someone did what we all do every now and then: implement a smaller subset of a formerly existing useful framework and provide only those functions that 95% of the projects really use. That is,
Add/remove classes, styles, HTML elements
Ajax communication
Model-based HTML iterator (lists)
Also there's sorting which is very useful and straightforward: you take one look and use it right away. It just works. That's what I love about w3.js - that, and the size, which seems to be 13k uncompressed-unminified. I wouldn't even bother to minify it.
I'm absolutely a fan of jQuery; it changed the way we think, became a de facto standard, and for a reason. But today most of the Sizzle part is implemented in the browser itself so the main advantages are available without jQuery.
I'm not sure why you want to ask this question, exactly; if you are concerned that jQuery is somehow inadequate, don't be: jQuery is a brilliant library and it will continue to be a part of the JS community for a long, long time.
W3Schools markets themselves as an easy-to-pick-up resource for brand new programming students, hence the cheerful coloring scheme on their site and the simplified language used in their articles. They are probably trying to cater to users who feel intimidated by the complexity of jQuery. Whether this attempt will be successful though, I cannot tell.
W3.js is a small, easy to troubleshoot helper library that has trimmed-back functionality. I use it in quick, toss-together apps because I find it easier to troubleshoot and document and it is easier to approach for people that are not familiar with jQuery's extensive feature set. It's kind of like a non-optimized, "culture-free" jQuery in that it depends very little on tribal/cultural knowledge to be easy to understand.
I don't see W3schools going anywhere, it's frequented by an endless supply of novice developers which seems to be the target for its w3.css and w3.js. If you do use it, I'd probably shy away from linking it in from their site/CDN. Probably best to have a local copy else risk your site becoming non-functional if/when the file become unavailable. I also wouldn't trust it in large-scale production applications just because as others have mentioned, it's likely not used enough by people that would notice problems with its code. I know I only use it for pet projects and honestly, I've never dug through the source. (I may now that I think about this though)

Input objects not shared across html files

I'm using jsPsych in behavioral research. The developer of that library is very helpful, yet also busy, so I wanted to try and see if the stack overflow community could help me out with a more general js problem :)
In the instance where I'm getting issues, I push objects into an empty array to update the site after input. In this particular case, I use a script that allows me to use external html pages. My problem is, that, while this function here works in order to correctly display a java prompt when assessing a checkbox
var check_consent = function(elem) {
if ($('#consent_checkbox').is(':checked')) {
return true;
}
else {
alert("If you wish to participate, you must check the box next to the statement 'I agree to participate in this study.'");
return false;
}
return false;
};
this here doesn't work in order to assess a text box
var inp = $("#ctry_box").val();
var check_sociodemo = function(elem) {
if ($.trim(inp).length > 0) {
return true;
}
else {
alert("Please fill out the form.");
return false;
}
return false;
};
More specifically, the prompt does actually work, but no matter what you type into "ctry_box", you can't continue the page and the prompt is shown no matter what the input.
Further, the developer set "data" as a object property designed to store data in accordance with individual variable choices. Regarding the same html files, I would like to gather the input from another text box like this
var sociodemo_block = {
type: 'html',
pages: [{url: "text/sociodemo.html", cont_btn: "end", check_fn: check_sociodemo}],
data: [{age: age_box.value}],
force_refresh: true
If I run this, the console tells me that age_box is not defined. Yet again, #consent_checkbox did work. Am I missing something fundamentally here or are the variables simply not shared across the files properly?
I'm very thankful for any help!

Handling asynchronous response in JS - limited access to JS files that can be edited

I am modifying a third party - web client application in which I only have access to certain js files.
The search function is limited to search in one given server node at a time, and as a work around, I hardcoded all the server nodes and created a for loop, invoking the "search" several times, at different nodes.
The server response (in a form of FORM - without getters) are automatically handled by a callback, which then renders the view of the form. This means I am only able to display the last response and thus displaying only one set of result.
To handle this, I added $trs = $(tr).clone(true) on the callback function, saving all the rows from previous forms and then - I made the last loop to "search" to have another callback - which will then append the collected rows from $tr and display the last form complete with all the results from all nodes.
But the result is inconsistent. It sometimes just displays result from one server node. I would think this is caused by some delay in server response which caused that form to render last. I tried to put delay by setTimeout function, but that keeps me from getting any result at all
I am very new with all the web programming - JS and JQUERY both (well CSS and HTML even lol) and I would like to ask for your suggestions on a better way to handle this.
Thank you!
_handleConfigSubmit: function (form, error) {
//alert("_handleConfigSubmit");
if (form) {
var formView = new jabberwerx.ui.XDataFormView(form);
var that = this;
formView.event("xdataItemSelected").bind(function(evt) {
that.jq.find(".muc_search_button_join").removeAttr("disabled");
var resultTable = that.jq.find(".muc_search_results table.result_table");
resultTable.find("tr.selected").removeClass("selected");
that._selectedItem = evt.data.selected;
resultTable.find("tr#"+evt.data.selected._guid).addClass("selected");
});
var searchResultsDiv = jabberwerx.$(".muc_search_results", this.jq);
searchResultsDiv.empty();
this.update();
var dim = {
width: searchResultsDiv.width(),
height: searchResultsDiv.height()
};
formView.render().appendTo(searchResultsDiv);
formView.dimensions(dim);
$trs = $("table.result_table tbody>tr:not(:first)").clone(true);
if ($trList!=null){
$trList = $trList.add($trs);
}else{
$trList = $trs;
}
$("table.result_table tbody>tr:not(:first)").remove()
if (ctr<=3){
$("table.result_table tbody").append($trList);
}else{
ctr++;
}
} else {
this._showError(error);
}
}

onClick replace /segment/ of img src path with one of number of values

No idea what I'm doing or why it isn't working. Clearly not using the right method and probably won't use the right language to explain the problem..
Photogallery... Trying to have a single html page... it has links to images... buttons on the page 'aim to' modify the path to the images by finding the name currently in the path and replacing it with the name of the gallery corresponding to the button the user clicked on...
example:
GALLERY2go : function(e) {
if(GalleryID!="landscapes")
{
var find = ''+ findGalleryID()+'';
var repl = "landscapes";
var page = document.body.innerHTML;
while (page.indexOf(find) >= 0) {
var i = page.indexOf(find);
var j = find.length;
page = page.substr(0,i) + repl + page.substr(i+j);
document.body.innerHTML = page;
var GalleryID = "landscapes";
}
}
},
There's a function higher up the page to get var find to take the value of var GalleryID:
var GalleryID = "portfolio";
function findGalleryID() {
return GalleryID
}
Clearly the first varGalleryID is global (t'was there to set a default value should I have been able to find a way of referring to it onLoad) and the one inside the function is cleared at the end of the function (I've read that much). But I don't know what any of this means.
The code, given its frailties or otherwise ridiculousness, actually does change all of the image links (and absolutely everything else called "portfolio") in the html page - hence "portfolio" becomes "landscapes"... the path to the images changes and they all update... As a JavaScript beginner I was pretty chuffed to see it worked. But you can't click on another gallery button because it's stuck in a loop of some sort. In fact, after you click the button you can't click on anything else and all of the rest of the JavaScript functionality is buggered. Perhaps I've introduced some kind of loop it never exits. If you click on portfolio when you're in portfolio you crash the browser! Anyway I'm well aware that 'my cobbled together solution' is not how it would be done by someone with any experience in writing code. They'd probably use something else with a different name that takes another lifetime to learn. I don't think I can use getElement by and refer to the class/id name and parse the filename [using lots of words I don't at all understand] because of the implications on the other parts of the script. I've tried using a div wrapper and code to launch a child html doc and that come in without disposing of the existing content or talking to the stylesheet. I'm bloody lost and don't even know where to start looking next.
The point is... And here's a plea... If any of you do reply, I fear you will reply without the making the assumption that you're talking to someone who really hasn't got a clue what AJAX and JQuery and PHP are... I have searched forums; I don't understand them. Please bear that in mind.
I'll take a stab at updating your function a bit. I recognize that a critique of the code as it stands probably won't help you solve your problem.
var currentGallery = 'landscape';
function ChangeGallery(name) {
var imgs = document.getElementsByTagName("img") // get all the img tags on the page
for (var i = 0; i < imgs.length; i++) { // loop through them
if (imgs[i].src.indexOf(currentGallery) >= 0) { // if this img tag's src contains the current gallery
imgs[i].src = imgs[i].src.replace(currentGallery, name);
}
}
currentGallery = name;
}
As to why I've done what I've done - you're correct in that the scope of the variables - whether the whole page, or only the given function, knows about it, is mixed in your given code. However, another potential problem is that if you replace everything in the html that says 'landscape' with 'portfolio', it could potentially change non-images. This code only finds images, and then replaces the src only if it contains the given keyword.

Trying to clean out form inputs with jQuery so I can add it back into the form

I have a pretty simple HTML form where users can enter in information about a person. Below that form is a button which allows them to 'add more'. When clicked, the 'person' form is copied and appended to the page.
The way I used to do this was to take my HTML file, copy out the relevant section (the part that gets 'added more') and then save it into a variable in the Javascript. This became rather annoying when I had to make changes to the form as I would then have to make the same changes to the Javascript variable.
My new method is to create the variable dynamically in Javascript. When the page loads, I use jQuery to grab out the 'add more' part of the code and cache the HTML into a variable. Then when the 'add more' button is clicked, I append that cached HTML to the page.
The problem is with form inputs. The server-side code autofills the form with the user's data from the database. I want to cache that HTML data with no form inputs...
My current function looks like this:
function getHTML($obj, clean)
{
if (clean)
{
var $html = $obj.clone();
$html.find('input').each(function() { $(this)[0].value = ''; });
}
else
{
var $html = $obj;
}
var html = $html.wrap('<div></div>').parent()[0].innerHTML;
$html.unwrap();
return html;
}
It doesn't work. I'm also unsure if this is the best approach to solving the problem.
Any ideas?
I don't know why this wouldn't work. I can't see how the function is being called, or what is being passed to it.
I guess one thing I'd do differently would be to create a .clone() whether or not you're "cleaning" the inputs. Then you're not wrapping and unwrapping an element that is in the DOM. Just use the if() statement to decide whether or not to clean it.
Something like this:
function getHTML($obj, clean) {
var $clone = $obj.clone();
if (clean) {
$clone.find('input').each(function() { this.value = ''; });
}
return $clone.wrap('<div></div>').parent()[0].innerHTML;
}
Or a little more jQuery and less code:
function getHTML($obj) {
return $obj.clone().find('input').val('').end().wrap('<div/>').parent().html();
}
A little less efficient, but if it only runs once at the page load, then perhaps not a concern.
Or if it is going to be made into a jQuery object eventually anyway, why not just return that?
function getHTML($obj) {
return $obj.clone().find('input').val('').end();
}
Now you've returned a cleaned clone of the original that is ready to be inserted whenever you want.
EDIT:
Can't figure out right now why we can't get a new string.
Here's a function that will return the DOM elements. Beyond that, I'm stumped!
function getHTML($obj, clean) {
var $clone = $obj.clone();
if (clean) {
$clone.find('input').each(function() {
this.value = '';
});
}
return $clone.get(); // Return Array of DOM Elements
}
EDIT: Works now.
I ditched most of the jQuery, and used .setAttribute("value","") instead of this.value.
Give it a try:
function getHTML($obj, clean) {
var clone = $obj[0].cloneNode(true);
var inputs = clone.getElementsByTagName('input');
console.log(inputs);
for(var i = 0, len = inputs.length; i < len; i++) {
inputs[i].setAttribute('value','');
}
return $('<div></div>').append(clone)[0].innerHTML;
}
I would wrap the part of the form that needs to be cloned in a <fieldset>:
<form id="my_form">
<fieldset id="clone_1">
<input name="field_1_1">
<input name="field_2_1">
<input name="field_3_1">
</fieldset>
</form>
Add one more
Then for the jQuery script:
$("#fieldset_clone").click(function(event) {
// Get the number of current clones and set the new count ...
var cloneCount = parseInt($("fieldset[id^=clone_]").size());
var newCloneCount = cloneCount++;
// ... then create new clone based on the first fieldset ...
var newClone = $("#clone_1").clone();
// .. and do the cleanup, make sure it has
// unique IDs and name for server-side parsing
newClone.attr('id', 'clone_' + newCloneCount);
newClone.find("input[id^=clone_]").each(function() {
$(this).val('').attr('name', ($(this).attr('name').substr(0,7)) + newCloneCount);
});
// .. and finally insert it after the last fieldset
newClone.insertAfter("#clone_" + cloneCount);
event.preventDefault();
});
This would not only clone and clean the set of input fields, but it would also set new ID's and names so once the form is posted, their values would not be overwritten by the last set.
Also, in case you want to add the option of removing sets as well (one might add too many by mistake, or whatever other reason), having them wrapped in a <fieldset> that has an unique ID will help in accessing it and doing a .remove() on it.
Hope this helps.

Categories