Trying out a small jquery plugin to make the user experience of html input file upload control consistent across browsers. Its basically a form which has an html input control which is nested in a hidden iframe.
The plugin exposes events like beforeUpload, afterUpload etc, and I am able to wire these correctly.
Here is a sample of how I'd invoke the afterUpload event.
$.fn.createUpload = function(config){
//stuff
$(myUploadIframe).load(function(){
var doc = this.contentWindow.document;
var txt = $(doc).text();
//parse txt to get json results
var result = frameWorkParser(txt);
if(result.uploadSuccess){
if(config.afterUpload){
config.afterUpload(sender, result);
}
}
});
//other stuff
};
Here is how I'd consume the plugin:
var config = {
afterUpload: function(el, data){
//set some hidden field
},
//other options
};
$('#btnSelectFile').createUpload(config);
If I've somehow invoked nested form (i.e inside the iframe) submit method, the iframe loads and I am able to successfully call the afterUpload event.
But here is where things get trickier. I am trying to execute some logic after all uploads are completed.
$.doUploads = function (callback) {
$('iframe').each(function () {
if (this.id.indexOf('_uploadContainer') > -1) {
var doc = this.contentWindow.document;
doc.forms[0].submit();
}
});
callback();
};
Here I am iterating all the plugin-created iframes (created for uploading) and invoking the nested form submit method. But this call doesn't wait for the consequent iframe onload event to complete, and the callback executes.
I want the callback to execute post the afterUpload event of my plugin. How to accomplish this?
Set some int variable(say totaliframeloaded=0;) which gets incremented on every iframe load. also give a call to callback function on every iframe load.
In callback check, whether that int variable has length equal to $('iframe').length. This condition will be getting matched when all the iframes gets loaded.
if(`totaliframeloaded==$('iframe').length){//all iframes are loaded
//callback coad
}else{
//do nothing/return
}
Related
I'm struggling with Apps Script's google.script.run response time. There is a function that returns user permission as boolean: initFunction() { // function code } returns true.
Some divs in my frontend are showing based on the initFunction boolean, and I don't know why the callback time is soo slow (like 4-5 seconds)
INDEX.HTML
<script>
function check_permission () {
google.script.run.withSuccessHandler(function(data){
if (data === true) {
document.getElementById('hidden_div').style.display = 'block';
} else {
document.getElementById('hidden_div').style.display = 'none';
}
}).initFunction();
}
document.addEventListener('DOMContentLoaded', () => { check_permission() });
</script>
I've tried calling initFunction just after sidebar load function just to check the function time and it returns true in 0.5 seconds, so it's not about the function, i suppose it's about google.script.run
function sidebarLoad() {
let route = HtmlService.createTemplateFromFile('index').evaluate();
SpreadsheetApp.getUi().showSidebar(route);
let permission = initFunction(); SpreadsheetApp.getUi().alert(permission)
}
How could I solve this and reduce response time?
Edits: after reading your comments I still don't know where is the problem but i've been doing tests and:
When calling the function from onclick event, the time response is very fast, so it's not about the function itself.
Answering #TheMaster, my start criteria for time response is when pressing the menu ui button that opens my GAS sidebar. The DOMContentLoaded function triggers immediately, I know because I changed the google.script.run in check_permission function with any other javascript code and it's loaded quicky. So I suppose it's not about DOMContent loading slowly.
If I click a button in the loaded html page that calls the function check_permission() I also get the results immediately. I only get the slow response time when google.script.run is triggered by DOMContentLoaded listenerEvent.
Try using templated html and load user permission as a global into the template before it's rendered.
function myFunction() {
let temp = HtmlService.createTemplateFromFile('filename');
temp.protection = initFunction();
let html = temp.evaluate();
}
Then in the html protection is a global variable and you can distribute it with scriptlets
Pushing variables into templates
I'm building a dynamic website that loads all pages inside a "body" div via jquery's load(). The problem is I have a script looped with setInterval inside the loaded PHP page, the reason being I want the script loaded only when that page is displayed. Now I discovered that the scripts keep running even after "leaving" the page (loading something else inside the div without refresh) and if I keep leaving / returning the loops stack up flooding my server with GET requests (from the javascript).
What's a good way to unload all JS once you leave the page? I could do a simple dummy var to not load scripts twice, but I would like to stop the loop after leaving the page because it's causing useless traffic and spouting console errors as elements it's supposed to fill are no longer there.
Sorry if this has already been asked, but it's pretty hard to come up with keywords for this.
1) why don't you try with clearInterval?
2) if you have a general (main) function a( ) { ... } doing something you can just override it with function a() { }; doing nothing
3) if you null the references to something it will be garbage collected
no code provided, so no more I can do to help you
This really sounds like you need to reevaluate your design. Either you need to drop ajax, or you need to not have collisions in you method names.
You can review this link: http://www.javascriptkit.com/javatutors/loadjavascriptcss2.shtml
Which gives information on how to remove the javascript from the DOM. However, modern browsers will leave the code in memory on the browser.
Since you are not dealing with real page loads/unloads I would build a system that simulates an unload event.
var myUnload = (function () {
var queue = [],
myUnload = function () {
queue.forEach(function (unloadFunc) {
undloadFunc();
});
queue = [];
};
myUnload.add = function (unloadFunc) {
queue.push(unloadFunc);
};
return myUnload;
}());
The code that loads the new pages should just run myUnload() before it loads the new page in.
function loadPage(url) {
myUnload();
$('#page').load(url);
}
Any code that is loaded by a page can call myUnload.add() to register a cleanup function that should be run when a new page is loaded.
// some .js file that is loaded by a page
(function () {
var doSomething = function () {
// do something here
},
timer = setInterval(doSomething, 1000);
// register our cleanup callback with unload event system
myUnload.add(function () {
// since all of this code is isolated in an IIFE,
// clearing the timer will remove the last reference to
// doSomething and it will automatically be GCed
// This callback, the timer var and the enclosing IIFE
// will be GCed too when myUnload sets queue back to an empty array.
clearInterval(timer);
});
}());
I would do this in JS fiddle, but I can't get the POST echoer to work, so I'll make an example here. Let's pretend that someApi returns "bar"
JS / jQuery
$(function() {
$('button').click(function(event) {
getSomeData();
});
function getSomeData() {
$("div").text("Foo = ");
$.get("someApi", function(i) {
$("div").append(i);
});
};
});
HTML
<div></div>
<button>Click Me</button>
There maybe some typos here, but please ignore them as I've written an example on-the-fly. What happens is when <button> is clicked once, all works well. The AJAX function is called and the <div> is appended when the response comes. If I wait for the response and click again, the <div> is overwritten with Foo = and then appended. The issue comes when the user becomes inpatient and clicks <button> multiple times, spawning multiple AJAX requests. This ends up with "bar" being appended multiple times. Is there a feature within JS / jQuery to avoid sending multiple requests to the same URL? I DON'T mean I want a async = false scenario; I know this would work, but would also slow the application down. I also know I could add an if loop that checks if bar has already been appended. What I'm asking for is a proper JS / jQuery .blockMultipleRequest(); kind of thing.
I don't think that there's a plugin for that. You could use .one() in this way:
function bindButton() {
$('button').one('click', function(event) {
getSomeData();
});
}
function getSomeData()
$("div").text("Foo = ");
$.get("someApi", function(i) {
$("div").append(i);
bindButton();
});
}
$(function() {
bindButton();
});
In function bindButton() you define your event handler with one(). Once button has been clicked event is removed until response of AJAX call, then function bindButton() is called again and event handler gets bound again.
You could use the global AJAX event handlers that jQuery provides and then do stuff depending on the request.
.ajaxSend() when any request starts (the event, jqXHR, and settings properties are sent to the handler, so you can then do URL-specific actions by evaluating settings.url
.ajaxComplete() when any request completes.
You could then use an object that keeps track of AJAX calls per URL, which can consult before sending off another request (e.g. only if it not currently in an active/pending state).
Hi
I am trying to do an auto complete functionality for an input field.
psuedo code
<input type="text"/>
<script>
var ref,resp;//Global Variables
$('input').live('keyup',function(){
/* Get the input offset, so that list container can be palced at the bottom of the input once get the values through Ajax call */
ajaxCall();
/***
Here I want to write a code to create a div and place the values with in the div and show at the respective input field.
***/
});
function ajaxCall(){
var ref = new XMLHttpRequest();
ref.open();
ref.readStateChange = function(){
if(ref.readyState == 4 && ref.status ==200)
resp = ref.responseText();
}
ref.send();
}
</script>
The problem that I am getting here is, the part of the code that is after ajax call should be executed once ajax readyState is 4 and values are retrived.
But that code is being executed when readyState is 1(Its not being called after the other states) where the values are not retrieved from the database.The lets me unable to show the list.
Note: I know that the below part can be put in ajaxCall but it contains some variables which can be set at the place....
Does my problem make sense? If so,can some body let me know the solution...
You have to call the functions that depend on the result of the AJAX call during the AJAX callback. That's just how it is:
function ajaxCall(callback) {
var ref = new XMLHttpRequest();
ref.open();
ref.readStateChange = function() {
if (ref.readyState === 4 && ref.status === 200) {
resp = ref.responseText();
callback.call(null, ref.responseText); // invoke the callback here
}
}
ref.send();
}
and then:
ajaxCall(function(resp) {
// handle "resp" here
});
That said, please don't re-invent the wheel. You'll end up with code that's hard to maintain and not portable across browsers. There's plenty of libraries that make AJAX code like this a complete doddle. I use jQuery.
The code that you expect to run after the completion of ajax call should be put in the onSuccess() or onComplete() method of your code.
ajaxCall(function(resp) {
/***
Here I want to write a code to create a div and place the values with in the div and show at the respective input field.
***/
});
i.e.. This part of your code must come in the onComplete() method, whic will have the data returned by the ajax call as parameter.
We're creating a click tracking app, that builds heatmaps. I'm writing a script which users are suppose to insert into their pages for tracking to work.
It works fine on elements, which doesn't require a redirect or form submit. For example, if I click on h1 or p or whatever, it works perfectly correct. But, if I click on a a, request to our server never happens before the normal redirect.
In the last couple of days I tried a lot of ways to do that. First of, I tried a normal AJAX call, since it was a cross-domain request I had to use JSONP, but again, that AJAX call did not have time to execute before the redirect. Adding async: false would have solved the problem, but it doesn't work with JSONP requests. So I decided to add a flag variable which indicates that it is safe to move on with redirect and used an empty while loop to wait until it becomes try in the ajax callback. But the while loop was blocking the execution flow, so callback never got a chance to set that variable to true. Here is some simplified code:
$(document).on('click', function (e) {
//part of the code is omitted
$.ajax({
url: baseUrl,
data: data,
type: "get",
dataType: "jsonp",
crossDomain: true,
complete: function (xhr, status,) {
itsSafeToMoveOn = true;
}
});
while(!itsSafeToMoveOn){}
return true;
});
The next thing I tried is to use unload page event to wait until total ajax calls in progress would become zero (I had a counter implemented) and then to move on with redirect. It worked in Firefox and IE, but in WebKit there was this error:
Error: Too much time spent in unload handler
After that I realized that I don't care about the server response and using img.src for the request would be an ideal fit for this case. So at this point code looks like this:
$(document).click(function (e) {
//part of the code is ommited
(new Image).src = baseUrl + '?' + data;
if (tag === "a" || clickedElement.parents().has("a")) {
sleep(100);
}
return true;
});
That way I increased the overall script performance slightly, but problem with links remains unchanged. The sleep function appears to be also blocking the execution flow and request never happens.
The only idea left is to return false from the event handler and than redirect manually to the clicked element's href or to call submit() on the form, but it will complicate things to much and believe me it's already a huge pain in the ass to debug this script in different browsers.
Does anyone have any other ideas?
var globalStopper = true;
$(document).on('click', function (e) {
if (globalStopper === false)
return true; //proceed with click if stopper is NOT set
else {
globalStopper = false; //release the breaks
$.ajax({
//blahblah
complete: function (xhr, status,) {
$(elem).click(); //when ajax request done - "rerun" the click
}
});
return false; //DO NOT let browser process the click
}
});
Also, instead of adding image, try adding script. And then add the script to the HEAD section. This way the browser will "wait" until it's loaded.
$(document).on('click', function (e) {
var scriptTag = document.createElement("script");
scriptTag.setAttribute("type", "text/javascript");
scriptTag.setAttribute("src", url);
document.getElementsByTagName("head")[0].appendChild(scriptTag);
return true;
}
I would take a look at the navigator sendBeacon API mentioned in this stack overflow answer or directly linked to here.
From the description on the site
navigator.sendBeacon(url, data) - This method addresses the needs of analytics and diagnostics code that typically attempts to send data to a web server prior to the unloading of the document.
You can save information to ajax request in cookies or localStorage and make any worker that will send information. Saving to cookies or localStorage is faster then ajax-request. You can do next:
$(document).click(function (e) {
var queue = localStorage.getItem('requestQueue');
queue.push(data);
localStorage.setItem('requestQueue',queue);
});
$(function(){
setInterval(function(){
var queue = localStorage.getItem('requestQueue');
while (queue.length > 0) {
var data = queue.pop();
$.ajax({
...
success: function(){
localStorage.setItem('requestQueue', queue);
}
});
}
},intervalToSendData);
});
So, when user click on link or send a form, data will be saved to storage and after user go to next page, this worker starts and send data to your server.
The JavaScript is basically executed in single thread. It is not possible to have your callback function executed and at the same time have an infinite loop waiting for a flag variable from it. The infinite loop will occupy the single execution thread and the callback will never be called.
Best approach is to cancel the default handler of your event and bubbling for it (basically return false if you are really building your tracking code with jQuery), and do the necessary actions (redirect page to the necessary address if a link was clicked or trigger other default actions), but this would take a lot of careful work to recreate all the possible combinations of actiona and callbacks.
Another approach is to:
1) Look for something specific to your code in the event data
2) If it is not present - make an AJAX call and in its callback re-trigger the same even on the same element, but this time with your specific bit added to the even data; after the AJAX call return false
3) If your specific bits are present in the data - simply do nothing, allowing the default event processing to take place.
The either approach may bite, however.
So if I understand right, you want your ajax logs completed before the page unloads and follows a link href. This sounds like a perfect case where you could consider using Deferreds in jQuery.
When your user clicks on anything that's supposed to take him away from the page, just check your promise status. If it's not resolved, you could throw a modal window over the page, and ask the user to wait til the progress is complete. Then, add a new pipe to your deferred, telling it to change the location href once everything is complete.
Let me know if this is the scenario. If it is, I'll explain in more detail. No use continuing if I didn't understand your requirement properly