I have some very basic code that acts as a loading gif for a webpage:
The loading and content containers sit in my base template. The
//header
<a href="{{ url_for('welcome', id=id, profile=profile) }}" onclick="spinner();" class="home">Home</a>
//more html
<div id="loading">
<img src="/static/images/Loading.gif" class="ajax-loader">
</div>
<div id="content">
{% block content %}{% endblock %}
</div>
<script type="text/javascript">
function spinner() {
console.log('fire');
$("#loading").show();
$("#content").hide();
}
</script>
div#loading {
height: 350px;
position: relative;
display: none;
background: white;
}
.ajax-loader {
position: absolute;
left: 0;
top: 0;
right: 0;
bottom: 0;
max-width: 100px;
margin: auto;
}
My code works perfectly well in firefox and chrome, but not in safari or on ios.
ps. I have checked that my function is working, the console shows 'fire' which i print straight after the onclick().
Edit
This is the only error in the console:
[Error] Failed to load resource: the server responded with a status of 404 (NOT FOUND) (jquery-ui.min.css, line 0)
Update 1
I have added a class to my <a href="url" class="..."
and updated my javascript to the following:
<script type="text/javascript">
$(function() {
$( ".spin_click" ).click(function( event ) {
event.preventDefault();
console.log('hiding content');
$(".content").hide();
console.log('show spinner');
$(".se-pre-con").show();
console.log('waiting now...');
});
});
</script>
#html
<div class="se-pre-con"></div>
<div class="content">
{% block content %}{% endblock %}
</div>
I can get safari to show the se-pre-con
div when I click on a link, however it completely disables the href link. Is it possible to pursue the link click having executed the .show() command in the jquery? Perhaps I have misunderstood preventDefault();
- but I thought it might be able to act as a workaround. The issue still only exists in safari
Update 2
As suggested by @maximast, I have decided to use addClass and removeClass. As usual it works in chrome and firefox, but not safari. The code is the same, I have just added a hide
class which is added and removed as necessary.
If I use the safari inspector and remove the hide class manually, the spinner does show up. When I click on a link in the page to trigger the jquery function, I can see the hide
class removed correctly from the div
. However, even though the hide
class is removed, the spinner does not appear. Perhaps I am missing something obvious here.
Here are the libraries i am using:
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/materialize/0.97.7/js/materialize.min.js"></script>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.15.0/moment.min.js"></script>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.2.2/Chart.min.js"></script>
Update 3
Here is a link to a test page. There is a link which puts the app to sleep for 3 seconds giving enough time for the spinner to trigger. The spinner sits in the logo. The test page will show the spinner working in a Chrome and Firefox, but not safari. The implementation with this button is exactly the same as used elsewhere in my app.
http://ec2-54-88-245-245.compute-1.amazonaws.com/spinner-test
I have changed the link in the logo to: <a href='#'
which for the first time render the spinner in all three browsers. (progress!). However when you click on the 'Click to trigger spinner.' link, the following happens:
- Chrome - spinner works first time.
- Firefox - spinner works first time, however there were a couple of occasions when I had to click it a second time.
- Safari - no luck.
Here is the flask code with the managing the spinner-test page:
@app.route('/spinner-test', methods=['GET'])
def spinner():
return render_template('spinner-test.html')
@app.route('/wait', methods=['GET'])
def wait():
time.sleep(3)
return redirect(url_for('spinner'))
I am unable to reproduce the issue with the code provided. However, I am adding an answer with a simple version of what you want to accomplish.
My guess is that the code not provided is more complex than you describe (or some dependencies are missing).
Since you already are using jQuery, I am giving you my answer using that library as well.
(updated: added other libraries, used by the OP, to test if their presence introduces the issue described)
See sample code below:
$(function() {
// add event listener to home (class) -
// used instead of inline binding using 'onclick'
$('.home').on('click', function() {
console.log('show spinner');
// show spinner right away
$("#loading").show();
console.log('waiting now...');
// simulate delay in fetching backend response (delay is 3 secs)
// contains the callback function to be invoked after your response is completed
setTimeout(function() {
// hide spinner
console.log('hide spinner');
$("#loading").hide();
// show content
console.log('show content');
$("#content").show();
}, 3 * 1000); // time is in ms so 1000ms = 1 sec (3*1000=3secs)
});
});
div#loading {
height: 50px;
position: relative;
display: none;
background: white;
}
.ajax-loader {
position: absolute;
left: 0;
top: 0;
right: 0;
bottom: 0;
max-width: 100px;
margin: auto;
}
#content {
display: none;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/materialize/0.97.7/js/materialize.min.js"></script>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.15.0/moment.min.js"></script>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.2.2/Chart.min.js"></script>
<header>
Header here
</header>
<hr/>
<a href="#" class="home">Home</a>
<div id="loading">
<img class="ajax-loader" src="https://i.stack.imgur.com/akN6H.png?s=48&g=1" alt="" width="24" height="24" class="avatar-me js-avatar-me">
</div>
<hr/>
<div id="content">
content will be loaded here
</div>
<footer>footer here
</footer>
It appears that you function is named spinner
, yet you're attempting to fire loading()
inside your a
tag (onclick="javascript:loading();
). Changing your function name to loading
should fix it.
Here's a working example:
https://jsfiddle.net/nvzge1e8/5/
Edit for updated question:
You need to prevent the event, if you stick event.preventDefault()
at the top of your function, it should play the animation. Do note, doing this will prevent your link from actually sending your user to the page, so you may need to add a timed redirect to whatever page you're sending them to.
Example code:
$(function() {
$(".spin_click").click(function(event) {
event.preventDefault()
$("#logo_spinner").attr("src", "/static/images/loaders/spinner.gif");
$('.content').addClass('hide');
setTimeout(function() {
window.location.href = "/wait";
}, 4000);
});
});
I have not tested this in Safari but you could try
$("#loading").fadeIn(0);
$("#content").fadeOut(0);
But make sure to remove the
display: none
from your CSS
Instead of $('elem').show()
and $('elem').hide()
try using...
$('elem').attr( 'data-display', 'block');
$('elem').attr( 'data-display', 'none');
In CSS add...
Attribute selector used twice to increase specificity ;)
[data-display][data-display='none'] {
display:none!important;
}
[data-display][data-display='block'] {
display:block!important;
}
Code will look like this...
$('h1').attr( 'data-display', 'block');
$('h2').attr( 'data-display', 'none');
[data-display][data-display='none'] {
display:none!important;
}
[data-display][data-display='block'] {
display:block!important;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<h1>I'm displayed</h1>
<h2>I'm hidden</h2>
<p>
I'm unaffected ;)
</p>
This will hide it (even if there is some css forcing visibility by using !important
), if not try increasing specificity of selector even more. Inspecting the element would help ;)
Sorry haven't tried reproducing your bug but unless you must use elem.hide()
and elem.show()
.
Alternatively, You can work with addClass and removeClass functions
$('#loading').addClass('hide');
$('#loading').removeClass('hide');
with .hide { display: none; }
works perfectly on safari too.
I am having the same issue as the OP, I am simply trying to have an element display only when on IOS, show/hide do not work, any combination of add class/remove class is not working either.
In my case I am working on progressive web application and this issue has eaten half of my day.
I even have an alert in the same function method that tries to fire the show, and/or remove class. This alert fires as expected but the element never appears. If I simply remove the hide class and reload the page it appears, if I change the element to show by default in CSS it works, but nothing I have tried so far gets the element to appear via JS. I have tried it in the CSS spreadsheet and with inline style. I've tried using visibility, display none/block, and simply hiding/unhiding via JS. Nothing works on IOS. Works fine in Chrome, Internet Explorer, Edge, Firefox, and various other browser. It just won't work in Safari on IOS and frankly I am stumped as to why.
If I ever get to the bottom of this issue I will post an answer but I have to say this is proving to be one of the more inexplicable situations that I have encountered in a 20 year development career.
!!UPDATE!!
Fixed it yes! After much stress, a colleague suggested it may be a popup blocker in Safari... he suggested tying it to a click in order to prove the concept.
At that precise moment an error modal popped up all on its own which led me to think... wait a minute - how does that work. It doesn't happen on page load! There is a delay!
And VOILA - wrapping the show functionality in a very small setTimeout function (10ms) solves the problem.
In my case I am adding functionality to replicate the Android PWA "add to home screen" functionality for IOS, so when the device is detected as IOS and when the PWA is not already on the home screen, the user is gently prompted to add it.