I'm trying to prevent defaults on a click, call a page with ajax and trigger the click on complete, using this answer.
<a id="mylink" href="file.csv" download >Dowload</a>
<script>
var flag = false;
$('#mylink').on('click',function(e) {
// Result is the same with :
//$(document).on("click","#mylink",function(e){
if (flag === true) {
flag = false;
return;
}
e.preventDefault();
$.ajax({
url: "index.php?controller=admin&action=refreshFile",
complete: function() {
console.log('control'); // This is called
flag = true;
$('#mylink').trigger('click'); // This is not called
}
});
});
</script>
The call works but the link is not triggered after. The result is the same when the ajax call is set inside a separate function.
use window.location to call the link href
$('#mylink').on('click',function(e) {
e.preventDefault();
$.ajax({
url: "index.php?controller=admin&action=refreshFile",
complete: function() {
console.log('control'); // This is called
window.location = $('#mylink').attr("href");
}
});
});
or with one event listeners
var eventListener = function(e) {
e.preventDefault();
$.ajax({
url: "index.php?controller=admin&action=refreshFile",
complete: function() {
console.log('control'); // This is called
$('#mylink')[0].click();
$('#mylink').one('click', eventListener);
}
});
};
$('#mylink').one('click', eventListener);
I'm not sure what your flag is supposed to do. In your example it would mean the link only works every 2nd click.
P.s. Using the complete callback means it also works even when the ajax fails. You might want to change it to success.
Update
#Racil Hilan has a point: this solution is a little overkill when you could just call the link directly and return the correct file after the refreshFile action has been called.
TRy
var flag = false;
$('#mylink').on('click',function(e) {
// Result is the same with :
//$(document).on("click","#mylink",function(e){
if (flag === true) {
flag = false;
windows.location="file.csv";
}
e.preventDefault();
$.ajax({
url: "index.php?controller=admin&action=fileDownload",
complete: function() {
console.log('control'); // This is called
flag = true;
$('#mylink').trigger('click'); // This is not called
}
});
});
In my humble opinion, this is not the right design. Your Ajax is calling the index.php on the server before triggering the download. If the index.php is doing some security or critical stuff that MUST be done before allowing the user to download the file, then this design is absolutely insecure. You don't even need to be a hacker, simply copy the link file.csv and paste it in your browser's address bar, and you'll get the file without the Ajax.
You need to place the file.csv file outside your website folder (or maybe it is generated on the fly by the server code, so that' good too) and then the PHP page must run all the checks and if all run OK, it reads the file (or generate it) and returns the download to the browser (or an error message if the checks failed). This is how to secure file downloads on the server.
After doing all of that, it is a matter of preference whether you call the PHP directly from your link, or the link calls the Ajax function which in turn calls the PHP page and parse the download (this is a bit more complex, but doable). The only difference between the two methods is whether you want the page refreshed when the download (or error message) come back from the server.
If you want to take this advice, rephrase your question and select which way you want to go (i.e. direct link, or through Ajax), so we can help you.
Related
I am designing a HTML page, and I would like to send a simple message (or trigger some action) when the user intentionally request updating (update button on the web browser, pressing F5... or whatever any other manual method that could exist) of the HTML file.
Something like:
window.onmanualupdaterequest = alert("You requested update");
Or whatever the correct procedure could be.
How could I do this?
Further notes:
I have tried the window.onbeforeunload function (example), but it does not exactly solve the problem (I would say it has not the same behavior as user request).
I would like to ignore the autoupdate case (like in setInterval or similar functions or scripts) from the manual update case. This question is about the manual one.
The classical Android swipe-down update method for a web page is considered here as a manual update method.
My idea would be to use the sessionStorage to save the window.location.href on pageload. If the user reloads the page the stored location should match the current url:
window.addEventListener('load', function () {
const lastUrl = sessionStorage.getItem('lastUrl');
if(lastUrl && lastUrl === window.location.href) {
alert("You requested update");
}
sessionStorage.setItem('lastUrl', window.location.href);
});
I'm trying to somewhat replicate what I saw in this question, particularly in this answer, but not quite the same.
My intent is, if the zip has no files (it can happen because the folder could be empty) I want to return an alert just so the user is warned that is not possible to obtain the file at the time.
But I'm missing on the redirection point, I don't want the alert to redirect the user to a blank page refering the Action, I want it to stay in the page, also due to some filters.
Is this possible? I couldn't find anything that would stop the redirection from happening.
Here is my the Action Controller code:
public ActionResult DownloadZip(List<int> things)
{
// Create zip with files
if (!zip.Any())
{
return Content(#"<script language='javascript' type='text/javascript'>
alert('Message');
</script>
");
}
// Return zip
}
Here is the call from the view:
$("#btnExportToZip").on("click", function (e) {
var grid = $("#gridThings").data("kendoGrid");
var items = grid.dataSource.data();
var lstIds = [];
$.each(items, function (index, elem) {
if (elem.Checked) {
lstIds.push(elem.Id);
}
});
if (lstIds.length > 0) {
var params = lstIds.join("&listAmostras=")
var url = '/Search/DownloadZip?listAmostras=' + params;
window.location.href = url;
}
});
If you do a redirect as you're doing here, it's too late to take it back once you've determined the zip file is empty. Your best bet here is probably to do an AJAX file download. Bear in mind, though, that this will require that the browser supports the HTML5 File API, so IE 9 and under are out.
$.ajax({
url: url,
async: false,
xhrFields: {
responseType: 'blob'
},
success: function (data) {
var a = document.createElement('a');
var url = window.URL.createObjectURL(data);
a.href = url;
a.download = 'myfile.pdf';
a.click();
window.URL.revokeObjectURL(url);
}
});
Essentially what this does is request the zip file via AJAX. Once the file data has been received, an anchor link is added to the DOM (not visible) and dynamically "clicked" to approximate the behavior of user click a link to a static file. In other words, a download prompt will pop as soon as the AJAX request completes successfully. However, this code only removes the need to redirect. You still need to conditionally pop the download only if the zip file has something in. There's two ways you can accomplish that.
In the success callback of the AJAX, you would wrap the code there in a conditional that checks that data.size > 0. However, that might not actually work. I've never looked at an empty zip file, but it's entirely possible that there's file headers in the binary that would cause the blob to actually have a size greater than zero, even though it's "empty".
The better approach is to return an error response in your zip action when the zip file is empty. Off the top of my head, I'm not sure what the most appropriate error response code would be, but anything in 400-500 range will work for triggering the appropriate AJAX callback. Then, you just need to add and error handler to this AJAX. In that handler, you could then notify the user however you like that there's no download because the zip would be empty.
As per my understanding, alert is redirect the user to the blank page because in the javascript you have the line window.location.href = url; which might be redirect to the same action again which shows the alert.
So try to give the different url to the window.location.href
for ex:window.location.href = '../somecontroller/someaction';
thanks
Karthik
What exactly is the magic that jQuery does that causes execution of JS code inside script tags found inside an AJAX response?
For example, when not using jQuery AJAX like below, I see use of eval() is frequently described as as a way to do this:
Calling a JavaScript function returned from an Ajax response
Can scripts be inserted with innerHTML?
So eval does the magic of code execution in those cases. I do not see the use of eval in jQuery AJAX calls.
jQuery Magic
$("#form").submit(function() {
$.ajax({
type : "POST",
url : 'process_form.php',
data : $("#form").serialize(),
success : function(data) {
$("#main_page").load('main_page.php');
}
});
return false;
});
Here jQuery sets up an event (form submit) to where clicking submit on a form submits the form and then it AJAX-loads the DIV with information returned from main_page.php (That page contains update information + JS + jQuery) into main_page div element. But, not just that.
It also triggers and runs and executes any JS/jQuery code located inside the script tags of the ajax-loaded data without any use of eval that I can see
Question: How does it do that?
Note:
I am using some 3rd party proprietary AJAX library, which properly loads up the response into main_page div but then it does not run any JS/jQuery.
Note 2:
to be clear, there is no problem at all with the jQuery code in question. It works and executes whatever JS was in the main_page.php. The proprietary AJAX library that I use loads up the page but does not execute any AJAX in it. I want to figure out what makes it not execute the code when jQuery executes the code.
3rd Paty Library that does not trigger script tag code execution
Delving into the library I see this:
http_req.open(method, url, true);
if (method == "POST") {
http_req.setRequestHeader("Content-type",
"application/x-www-form-urlencoded");
http_req.setRequestHeader("Content-length", post_str.length);
http_req.setRequestHeader("Connection", "close");
http_req.send(post_str);
} else if (method == "GET") {
http_req.send(null);
}
Library is called using something like this event on a submit button:
onclick="process_form(..., url, 'main_page'...)"
It also has this:
var responseObj = new getObject(response);
responseObj.obj.innerHTML = http_req.responseText;
Full code surrounding the above:
http_req.onreadystatechange = function() {
if (http_req.readyState == 4 || http_req.readyState == "complete") {
if (response != "" && redir == "") {
if (response == "document") {
document.write(http_req.responseText);
} else {
//Update of innerHTML (but without triggering execution)
//of code found in <script> tags of http_req.responseText
responseObj.obj.innerHTML = http_req.responseText;
loadingObj.style.display = "none";
}
} else if (response != "" && redir != "" && response != "over") {
load_page(redir, "", response, "GET", "");
}
}
}
My guess is that it updates innerHTML but does not ...
add it into the DOM?
do eval?
do the magic?
What is the magic that jQuery possesses that the above library skips out?
Is there a way I can fix the library to cause it to execute the code found in response script tag?
Do you know that this callback is executed in your browser, right? This have nothing to do with your server response. It only executes WHEN your server gives you data back.
You can see proper documentation on XHR here https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/Using_XMLHttpRequest
i hope i understand your question correctly:
success : function(data) {
$("#main_page").load('main_page.php');
}
this is the so called "success handler" - it is an event listener, that waits until the http response is received from the server. Javascript works a lot with callback functions which means, you call an action (like the ajax request) and once finished, it calls back another (often anonymous) function.
http://www.w3schools.com/js/js_events.asp
http://javascriptissexy.com/understand-javascript-callback-functions-and-use-them/
jQuery uses its own Deferred's to implement callbacks including for its ajax implementation. See the code here: https://github.com/jquery/jquery/blob/master/src/ajax.js
I have an html file with many <a> tags with href links.
I would like to have the page do nothing when these links point to an outside url (http://....) or an internal link that is broken.
The final goal is to have the html page used offline without having any broken links. Any thoughts?
I have tried using a Python script to change all links but it got very messy.
Currently I am trying to use JavaScript and calls such as $("a").click(function(event) {} to handle these clicks, but these have not been working offline.
Also, caching the pages will not be an option because they will never be opened online. In the long run, this may also need to be adapted to src attributes, and will be used in thousands of html files.
Lastly, it would be preferable to use only standard and built in libraries, as external libraries may not be accessible in the final solution.
UPDATE: This is what I have tried so far:
//Register link clicks
$("a").click(function(event) {
checkLink(this, event);
});
//Checks to see if the clicked link is available
function checkLink(link, event){
//Is this an outside link?
var outside = (link.href).indexOf("http") >= 0 || (link.href).indexOf("https") >= 0;
//Is this an internal link?
if (!outside) {
if (isInside(link.href)){
console.log("GOOD INSIDE LINK CLICKED: " + link.href);
return true;
}
else{
console.log("BROKEN INSIDE LINK CLICKED: " + link.href);
event.preventDefault();
return false;
}
}
else {
//This is outside, so stop the event
console.log("OUTSIDE LINK CLICKED: " + link.href);
event.preventDefault();
return false;
}
}
//DOESNT WORK
function isInside(link){
$.ajax({
url: link, //or your url
success: function(data){
return true;
},
error: function(data){
return false;
},
})
}
Also an example:
Outside Link : Do Nothing ('#')
Outside Link : Do Nothing ('#')
Existing Inside Link : Follow Link
Inexistent Inside Link : Do Nothing ('#')
Javascript based solution:
If you want to use javascript, you can fix your isInside() function by setting the $.ajax() to be non asynchronous. That is will cause it to wait for a response before returning. See jQuery.ajax. Pay attention to the warning that synchronous requests may temporarily lock the browser, disabling any actions while the request is active (This may be good in your case)
Also instead of doing a 'GET' which is what $.ajax() does by default, your request should be 'HEAD' (assuming your internal webserver hasn't disabled responding to this HTTP verb). 'HEAD' is like 'GET' except it doesn't return the body of the response. So it's a good way to find out if a resource exists on a web server without having to download the entire resource
// Formerly isInside. Renamed it to reflect its function.
function isWorking(link){
$.ajax({
url: link,
type: 'HEAD',
async: false,
success: function(){ return true; },
error: function(){ return false; },
})
// If we get here, it obviously did not succeed.
return false;
}
Python based solution:
If you don't mind preprocessing the html page (and even caching the result), I would go with parsing the HTML in Python using a library like BeautifulSoup.
Essentially I would find all the links on the page, and replace the href attribute of those starting with http or https with #. You can then use a library like requests to check the internal urls and update the appropriate urls as suggested.
Here is some javascript that will prevent you from going to external site:
var anchors = document.getElementsByTagName('a');
for(var i=0, ii=anchors.length; i < ii; i++){
anchors[i].addEventListener('click',function(evt){
if(this.href.slice(0,4) === "http"){
evt.preventDefault();
}
});
}
EDIT:
As far as checking if a local path is good on the client side, you would have to send and ajax call and then check the status code of the call (infamous 404). However, you can't do ajax from a static html file (e.g. file://index.html). It would need to be running on some kind of local server.
Here is another stackoverflow that talks about that issue.
This question already has answers here:
XHR request is denoted as being cancelled although it seems to be successful [closed]
(2 answers)
Closed 9 years ago.
I'm getting a "canceled" status whenever I do a jquery $.post(). It seems like an asynchronous problem? I access the local project with http://127.0.0.1:8933/myproject/default/index.html
index.html:
<script>
$(document).ready(function() {
$('#mybutton').click(function() {
var post_url = get_url(); // resolves to "/myproject/default/process_func"
var data = ...;
do_action(post_url, data);
});
});
</script>
Click me!
util.js:
function doPost(url, data) {
$.post(url, data).then(doSuccess, doFail):
function doSuccess(data) {
alert('Successful!');
}
function doFail(data) {
alert('Failed!');
}
}
function do_action(url, data) {
var jsondata = JSON.stringify(data);
// some other stuffs...
doPost(url, jsondata);
}
The idea is to post some data back to the server for processing using json data. A user will click on the anchor button, the do_action() will fire, and then it'll be posted in doPost().
This seems to work since my I'm seeing the json data passed to my processing function on the server. However, I'd see the alert message "Failed!" pop up every time even though the data has been processed. In chromium's debug panel, I see under "Network" tab that the post has a status of "(canceled)" and the entire line would be highlighted in red. The Type stated is "Pending".
I think this is an asynchronous problem, but I'm unsure how to fix it.
Use this, to cancel the default navigatin behavior of the <a>:
$('#mybutton').click(function(e) { // <-- Add `e` here
e.preventDefault(); // <-- Add this line
var post_url = get_url();
var data = ...;
do_action(post_url, data);
});
I believe the AJAX request is being aborted by the browser because when clicking the link, although the request does get sent off, the link tries to navigate to a new page so the browser must abort open AJAX requests.
You can try this...
$('#mybutton').live("click", function(e) {
e.preventDefault();
var post_url = get_url();
var data = ...;
do_action(post_url, data);
});
Depending on the version of jQuery you are using, you can use jQuery.live() or jQuery.on() method
When the e.preventDefault() method is called if this method is called, the default action of the event will not be triggered.
Resources:
event.preventDefault()
$.live()