Firefox CSS3 transition not working when toggled from external js file - javascript

I know there are bunch of questions already about CSS3 transitions not working in firefox. I think this one might be quite unique. As the transitions DO work when added into the relevant click functions within $(document).ready() . But once I've put the same code out into a basic js class and instantiated within $(document).ready it refuses to work in firefox 24. Works in all other major browsers.
Can anyone shed some light.
I've put up a test site here: http://test.gladekettle.com.au/modal/
var ModalApp = function (options) {
//default options
var defaultOptions = {
modalElem : "#modalresponsive",
z : 99,
overflowvisible : true,
scrollbardelay : 1000
}
for (i=0; i < options.length;i++){
if(typeof options[i] === "undefined"){ options[i] = defaultOptions[i];
}
} //for - set default options if not defined.
var $modalObj = $(options.modalElem);
var zindex = options.z;
$modalObj.css('z-index', zindex);
$modalObj.addClass('modalhidden');
this.close = function() {
$modalObj.removeClass('scrollvisible');
$modalObj.removeClass('modalvisible').addClass('modalhidden');
$('.modalactive').removeClass('modalactive');
};
this.open = function() {
$('html').addClass('modalactive');
$modalObj.removeClass('modalhidden').addClass('modalvisible');
setTimeout(function() {
$modalObj.addClass('scrollvisible');
}, options.scrollbardelay);
};
};

This has nothing to do with "external js files". The culprit is .modalactive { overflow: hidden; }, which gets set/unset to html on click and seems to be canceling out the transitions for whatever reason.
You may verify this by disabling the rule, either live on your site with the developer tools or see this fiddle.
You should file a bug and stop using that rule.

Related

IFrame scrollbar disappears on chrome when visibility changes

Is Google Chrome on Windows having problems to render iframe scrollbars?
I wrote a very simple code to show what is happening (at least with me on chrome 52.0.2743.82 m):
<button>Toggle visibility</button>
<br />
<iframe scrolling="yes" seamless src="https://en.wikipedia.org/wiki/Lorem_ipsum" frameborder="0" style="width: 700px; height: 300px"></iframe>
<script type="text/javascript">
$("button").on("click", function() {
$("iframe").toggle();
});
</script>
Plunker link to code
When the page is loaded, the iframe as it scrollbar are visible.
Hide and show the iframe clicking the button. The scrollbar disappears.
This issue apparently occurs only in chrome.
Anyone is experiencing this too? Any fixes/workarounds?
It seems that bug appeared with the update Chrome 52.0.2743.82 (http://googlechromereleases.blogspot.fr/2016/07/stable-channel-update.html)
One possible workaround is to use the attribute visibility with position: absolute instead of display to show or hide the iframe.
A chrome bug ticket exists for this item: https://bugs.chromium.org/p/chromium/issues/detail?id=641881
I've had this problem, and using visibility instead of display: none wasn't an option.
My workaround was to set overflow: scroll on the <body> of the document being displayed in the iframe, whenever I set the iframe to be visible again. This seems to force the scrollbar to appear on the iframe again. You can then reset the overflow to its old value, and the scrollbar will remain on the iframe. You need to wait for a repaint before you can reset the overflow, though, so I put this in a timeout with delay 0.
function showIframe(iframe) {
var iframeBody = iframe.contentDocument.body;
$(iframe).show();
var oldOverflow = iframeBody.css("overflow");
iframeBody.css("overflow", "scroll");
window.setTimeout(function () {
iframeBody.css("overflow", oldOverflow);
}, 0);
}
There is a "flash" of scrollbar with this workaround if the iframe in question doesn't need to scroll, though, so it might be worth using the visibility workaround for that brief moment where the repaint is required, to avoid the flash.
Here's a workaround I've developed for an application I'm building. It has multiple <iframe> elements in a Foundation tab-control.
I used MutationObserver to observe when the <iframe>'s parent element (a Foundation div.tabs-content div.content element) becomes active, then I toggle the iframe's document's overflow property. The runtime effect is imperceivable.
I originally wanted to observe the <iframe> directly, however no DOM mutation events were raised when the iframe itself's changed display property, I guess because technically speaking element.style values are not part of the DOM-structure proper.
Here's my code (Vanilla.js, no jQuery). If you're using in your application you will want to replace my visibility-detection code with something that is applicable to your document:
window.addEventListener('DOMContentLoaded', function(e) {
var observer = new MutationObserver( onContentMutated );
var options = { attributes: true, childList: false, characterData: false, subtree: false, attributeFilter: ['class'] };
var iframeContainers = document.querySelectorAll('.tabs-content .content');
for(var i = 0; i < iframeContainers.length; i++) {
observer.observe( iframeContainers[i], options );
}
});
function onContentMutated(mutations) {
for(var i = 0; i < mutations.length; i++) {
var m = mutations[i];
var thisIsNowAnActiveTab = m.target.classList.contains('active');
if( thisIsNowAnActiveTab ) {
// get the corresponding iframe and fiddle with its DOM
var iframes = m.target.getElementsByTagName("iframe");
if( iframes.length == 0 ) continue;
var iframe = iframes[0];
iframe.contentWindow.document.documentElement.style.overflow = 'hidden';
// the timeout is to trigger Chrome to recompute the necessity of the scrollbars, which makes them visible again. Because the timeout period is 0 there should be no visible change to users.
setTimeout( function(s) {
s.overflow = 'auto';
}, 0, iframe.contentWindow.document.documentElement.style );
}
console.log( m.type );
}
}
For the given example you can do:
$("iframe").toggle(1)
In my case, it worked by setting back the height:
$("iframe").css("height", "100%")
I had a similar issue on Chrome with an iframe embedded into a jQuery UI tab. When the tab containing the iframe is first displayed, the scrollbar appears. But when I switch to another tab and back to the tab with the iframe then the scrollbar disappears. All the solutions proposed here didn't work for me.
Here is what I did to fix the issue :
First, I create the tabs :
$("#mytabs").tabs();
Then I bind a function to the event "tabsactivate" and I check if the target tab is the one containing the iframe. If it is the case I call a function fixChromeScrollBar() described later on :
$("#mytabs").on("tabsactivate", function(event, ui) {
if ($(event.originalEvent.target).attr("href") == "#mytab-with-iframe") {
fixChromeScrollBar();
}
});
And finally here is the function fixChromeScrollBar() which sets the overflow style attribute of the iframe body (as already said) to either "scroll" or "auto". I noticed that when I only define the "auto" or "scroll" value then if I switch to another tab and back to the iframe I lose the scrollbars. The only way to maintain them is to alternate between the two values each time the iframe appears. It is weird but it works :
function fixChromeScrollBar() {
var iFrameBody = $("#myiframe").contents().find("body");
var originalOverflow = $(iFrameBody).css("overflow");
if (originalOverflow == "visible" || originalOverflow == "auto") {
$(iFrameBody).css("overflow", "scroll");
} else {
$(iFrameBody).css("overflow", "auto");
}
}
You can notice that this method is called only if you switch to the tab containing the iframe so if you click multiple times on this tab without switching to another one this code will only be executed the first time.
Apparently setting src refreshes iframe in chrome, for given example code will be:
<script type="text/javascript">
$("button").on("click", function() {
$('iframe').toggle().attr('src', function(i, val) { return val; });
});
</script>
I adapted Dai's example to my own React IFrame component. I have an iframe within a tab panel, which is itself in a collapsible panel. When either of those get toggled, I force the iframe to repaint. It works wonderfully.
private iframe: HTMLIFrameElement;
private displayObserver: MutationObserver;
componentDidMount() {
// Detect style attribute changes for the containing collapsible components
// If the display goes from 'none' to something else, then we need to redraw the iframe
// so we get the scrollbar back as it should be.
if (isChrome()) {
this.displayObserver = new MutationObserver(this.onContentMutated);
const options = { attributes: true, childList: false, characterData: false, subtree: false, attributeFilter: ['style'] };
const tabPanelAncestor = this.findAncestor(this.iframe, '.tab-panel-content');
if (tabPanelAncestor) {
this.displayObserver.observe(tabPanelAncestor, options);
}
const collapsibleAncestor = this.findAncestor(this.iframe, '.collapsible');
if (collapsibleAncestor) {
this.displayObserver.observe(collapsibleAncestor, options);
}
}
}
private readonly onContentMutated = (mutations: Array<MutationRecord>) => {
R.forEach( (mutation) => {
const targetElement = mutation.target as Element;
const style = targetElement.getAttribute('style');
if (style && !style.match(/display: none/)) {
this.iframe.contentWindow.location.reload(true);
}
}, mutations);
}
private readonly findAncestor = (element: HTMLElement, sel: string): Node | null => {
if (typeof element.closest === 'function') {
return element.closest(sel) || null;
}
let ancestor: HTMLElement | null = element;
while (ancestor) {
if (ancestor.matches(sel)) {
return ancestor;
}
ancestor = ancestor.parentElement;
}
return null;
}

How to fadeIn element on page load instead of "appear"?

Im a really huge noob on jquery, I need to figure out how to change this code:
$('.social li').appear();
$(document.body).on('appear', '.social li', function(e, $affected) {
var fadeDelayAttr;
var fadeDelay;
$(this).each(function(){
if ($(this).data("delay")) {
fadeDelayAttr = $(this).data("delay")
fadeDelay = fadeDelayAttr;
} else {
fadeDelay = 0;
}
$(this).delay(fadeDelay).queue(function(){
$(this).addClass('animated').clearQueue();
});
})
});
to work in the way that it would start animation as soon as someone enters the landing page, right now it works good on everything besides IE10 and IE11, was told to change it to load by default not on "appear" but I tried document ready/load and I can't get it to work...
You could try fading all list items into view, each with a progessing 250ms delay:
$(window).load(function() {
$('.social li').hide().each(function(i) {
$(this).delay((i + 1) * 250).fadeIn(2000);
});
});
EDIT:
Using the same logic as your previous code to refactor, use the window.load method since the load event fires at the end of the document loading process. At this point, all of the objects in the document are in the DOM, and all the images and sub-frames etc have finished loading. So use this event to do the fading in animation of the list items into view, where their initial state will be hidden.
You have two variables declared fadeDelayAttr and fadeDelay but I noticed that only fadeDelay is being used, so fadeDelayAttr can be discarded. Also, this part of the code:
if ($(this).data("delay")) {
fadeDelayAttr = $(this).data("delay")
fadeDelay = fadeDelayAttr;
} else {
fadeDelay = 0;
}
can be simplified as the null-coalescing operator using a logical OR (||):
var fadeDelay = $(this).data("delay") || 0;
Since the fadeDelay variable gets its value from the data-delay attribute, this can then be passed in as an argument for the delay method and finally the refactored code will look like this:
$(window).load(function() {
$('.social li').hide().each(function() {
var fadeDelay = $(this).data("delay") || 0;
$(this).delay(fadeDelay).fadeIn(2000);
});
});
Working Demo

CSS Animations and page refresh on previous page with iOS/Android

I'm trying to achieve a transition effect when changing pages on a project I'm working on. The desired effect is the following:
Page fades in with an opacity from 0 to 1, thanks to a #keyframes
On any link clicked, the page opacity is changed back to 0, with another #keyframes
I've added an animationEnd event listener which will force the opacity of the page to 0 (in order to avoid a weird flash) and then will go to the link.
This is working fine on the latest versions of Chrome, FF and IE, but I'm having issues with iOS and Android. When hitting the "back" button, the page is showed with its latest state (opacity: 0). I believe this is a native solution which forces the CSS/JS not to reload again, but it's quite annoying as I can't find a way to "refresh" assets when hitting the "back" button.
Does anyones have a solid solution for this kind of issue ?
--
As an example, I've copied below a sample of my current JS :
if ("animation" in document.body.style) {
var animationEnd = "animationend"
}
else {
var animationEnd = "webkitAnimationEnd"
}
var link = document.querySelectorAll('a');
for (var i = 0; i < link.length; i++) {
link.item(i).addEventListener("click", function(e) {
var linktarget = this.getAttribute('href');
if (linktarget != '#') {
e.preventDefault();
page.className += ' ' + 'fadeout';
var fadeout = document.querySelector('.fadeout');
fadeout.addEventListener(animationEnd, function() {
this.style.opacity = '0';
window.location.href = linktarget;
});
}
});
}
Try wrapping your code you want to "refresh" into pageshow:
window.addEventListener("pageshow", function() {
... your code ...
}, false);

Refresh Skrollr after resizing

I want the <nav> to become fixed after the user passes the first "block" (height: 100%;). I decided to use Skrollr because it is the only way I know to make the "change" immediately, without bugs on mobile and in IE. So I did this:
$("nav").attr("data-" + $("header").height(), "position: fixed;");
This is works great (the <nav> is right after the <header>), until you resizes the page. So I did this:
$(window).resize(function () {
var style = $("nav").attr("style");
$('nav').each(function() {
var attributes = this.attributes;
var i = attributes.length;
while( i-- ){
this.removeAttributeNode(attributes[i]);
}
})
$("nav").attr("data-" + $("header").height(), "position: fixed;");
$("nav").attr({"data-0": "position: absolute;", "style": style});
});
It takes the new height, and add it as a attr and deletes all others attr (because if not it will add you a lot: data-500, data-501, data-502, data-503...) and by looking at the code - it works great. The problem is that the Skrollr doesn't "sees" the change. what should I do?
Well, Thats was easy. One line instead of 20 lines.
var s = skrollr.init({
constants: {
menuresize: function() {
return $("header").height();
},
vh: '100p'
}
});
and to the nav I add data-_menuresize="position: fixed;" (and it explaines what menuresize
means in the code above).
Enjoy :D!

Javascript in IE7 and IE8

I have two sliders on my homepage, one is using a plugin (AnythingSlider), and the other is just using jQuery UI (jquery-ui-1.8.9.custom.min.js). After I added the AnythingSlider to the page, the hover function on the second slider stopped working in IE7 and IE8. This is the code for the hover function:
st = null;
$('.homepage-leftscroller').hover(function() {
slideleft();
st = setInterval(slideleft, 200);
}, function() {
clearInterval(st);
});
And here is the code for the slideleft function:
function slideleft() {
v = s.slider('option', 'value');
if (v > 0)
v -= 50;
ui.value = v;
s.slider('option', 'value', v);
f = s.slider('option', 'slide');
f(null,ui);
}
I didn't create the second slider, I did add the first slider. The page in question is here:
http://rareculture.net/index.php
I appreciate any help that can be offered with this. Thank you.
P.S.
The code block containing the hover function begins like this:
$(function() {
slider = $('.artist-homepage-slider .artist-wrapper');
while the AnythingSlider code block begins like this:
$(function(){
$('#slider')
Could that be the issue? Why would it only break in IE7 and IE8?
I can't tell if this is related to your problem, but the following statements are assigning values to global (or at least, from a wider scope), rather than local, variables. If you add var in front of these they'll become declarations and they'll be declared locally.
st = null;
v = s.slider('option', 'value');
f = s.slider('option', 'slide');
jslint can detect issues like these.

Categories

Resources