I'm learning Angular and there is something I dont really understand. My Application works fine on all browser (Chrome, Firefox, Brave, Opera, mobile version also) except Safari (desktop and mobile). My app launches great except for my script from the assets folder. ("src/assets").
angular.json
...
"scripts": ["src/assets/js/layout.js"]
...
layout.js
'use strict';
var layoutInit = function layoutInit(){
const header = document.querySelector("header")// return null on safari;
const main = document.querySelector("main") // return null on safari;
const footer = document.querySelector("footer") // return null on safari;
function resize(){
main.style.minHeight = window.innerHeight - (footer?.offsetHeight ?? 0) + "px";
main.style.paddingTop = (header?.offsetHeight ?? 0) + "px";
}
window.onresize = resize;
resize();
}
if(document.readyState === 'loading' || document.readyState === 'interactive'){
document.addEventListener('DOMContentLoaded', layoutInit)
}else{
layoutInit()
}
Safari version : 15.6.1
Angular : 15
Does anyone experienced this issue ?
First off: I can't answer the question. My guess is something to do with how/when Safari loads scripts?
However, I'd rather suggest alternatives as a better practice over what you currently have (unless you have it for a particularly good reason?); It is odd to have a lovely Angular SPA that can do everything you want, but then use an outside script to just set out the page layout.
Move that into the app.
Assuming you have a basic app.component.html that looks suspiciously like this currently:
<div id="header">...</div>
<div id="main">
<router-outlet></router-outlet>
</div>
<div id="footer>...</div>
Add in some view child usage:
<div id="header" #header>...</div>
<div id="main" #main>
<router-outlet></router-outlet>
</div>
<div id="footer" #footer>...</div>
So they can be accessed in the app.component.ts:
#ViewChild('header') public header: ElementRef;
#ViewChild('main') public main: ElementRef;
#ViewChild('footer') public footer: ElementRef;
And then handle your resize in there too:
#HostListener('window:resize'. []) public onResize() {
this.resizeLayout();
}
private resizeLayout(): void {
if (!this.main) return; // make sure it all exists first
this.main.style.minHeight = window.innerHeight - (this.footer?.offsetHeight ?? 0) + "px";
this.main.style.paddingTop = (this.header?.offsetHeight ?? 0) + "px";
}
Something like that; might look a little different but that's a you problem not a me problem.
Probably want to put in either an ngOnInit or likely an ngAfterViewInit method to call that so it's all set up on first load (otherwise it'll ONLY be triggered on window resizing)...
public ngAfterViewInit(): void {
this.resizeLayout();
}
That gets rid of your additional script and keeps it fully contained inside the app itself - triggered once when the app (view) is first initialised, and then subsequently after any window resizing is done.
That's a lot of effort for something that CSS can do, though.
Use CSS - flex
My preference is to use Angular flex layout rather than raw CSS because 1) it's easier and 2) I personally like to keep base page structural details with the base page itself (the HTML).
Because of that, my memory for base CSS flex details is pretty poor, so I'll give it to you in AFL form in the template:
<div id="app-container"
fxFlexFill
fxLayout="column"
fxLayoutAlign="stretch stretch">
<div id="header" fxFlex="100px">...</div>
<div id="main" fxFlex>
<router-outlet></router-outlet>
</div>
<div id="footer fxFlex="50px">...</div>
</div>
So long as you appropriately set up your html/body styling to flex layout, this will:
fxFlexFill: cause the app-container to fill the available space (e.g. if you had body { width: 100vw; height: 100vh; } then app-container now fills the whole of the window)
fxLayout="column": cause the child elements to be rendered down the page (row for across)
fxLayoutAlign="stretch stretch": cause the child elements to stretch up and down as well as across
Followed by the children:
fxFlex="100px": limit the header div to only 'stretch' to 100px
fxFlex: allow the main div to stretch as much as possible
fxFlex="50px": limit the footer div to only 'stretch' to 50px
There's some additional things needed to make it work a little nicer and work exactly how you want it to (e.g. #main { overflow: auto; }) but that gets you 90% of the way there with the layout you want and there's no actual scripting involved, just CSS.
Related
Say I've got a component with identical content, but presented two totally different markup structures depending on the device (desktop viewports or mobile viewports).
In this situation when the viewport is below or above certain width or breakpoint (for this example 768px), I want to show one over the other.
A common situation for something like this might be the Navigation, where at Desktop views you have a simple navigation bar in the header of the page, whilst at Mobile views you have a more complex navigation menu that slides in and out:
import React from 'react';
import './Navigation.scss';
const Navigation = () => {
return (
<div className="navigation">
<div className="mobile-navigation-container">
<MobileNavigation />
</div>
<div className="desktop-navigation-container">
<DesktopNavigation />
</div>
</div>
);
};
Solution 1:
A simple solution to achieve this is to use CSS:
.navigation {
.mobile-navigation-container {
#media (min-width: 768px){
display: none;
}
}
.desktop-navigation-container {
#media (max-width: 767px){
display: none;
}
}
}
However, the issue here is that I still have both views in the DOM, even though one is not visible.
Solution #2:
Alternatively, I can use a resize listener and piece of state in my JSX component to conditionally render the correct component depending on the viewport width I can calculate using window.innerWidth:
import React, { Component } from 'react';
const isClient = typeof window !== 'undefined';
class Navigation extends Component {
state = {
viewportWidth: 0,
}
componentDidMount() {
if (isClient) {
this.updateWindowDimensions();
window.addEventListener('resize', this.updateWindowDimensions);
}
}
componentWillUnmount() {
if (isClient) window.removeEventListener('resize', this.updateWindowDimensions);
}
updateWindowDimensions = () => {
this.setState({ viewportWidth: window.innerWidth });
}
render() {
const { viewportWidth } = this.state;
return (
<div className="navigation">
{viewportWidth <= 768 && (
<div className="mobile-navigation-container">
<MobileNavigation />
</div>
)}
{viewportWidth > 768 && (
<div className="desktop-navigation-container">
<DesktopNavigation />
</div>
)}
</div>
);
}
This solves the issue of having duplicate content on the DOM. Which I'd guess is better for Search Engine Crawlers.
However, this somewhat makes my JSX more complicated, and I have the feeling that the CSS breakpoint is cleaner, smoother implementation in terms of performance, rather than using a JavaScript resize listener (though I can't find solid sources to advise one over the other).
My question is which of these two implementations is better practice and why?
The second approach Solution #2 is very good as compared to Solution #1. Because #1 has unnecessary and unwanted elements in DOM Object which is also confusing for react. Although it is not a good practice in any other languages as well. But in #2 you are not rendering unwanted contents this will improve smooth running of your code and debugging and designing is also easy in this approach.
Definitely the 2nd one even though it involves more lines of code, the overall performance outcome is much better because you don’t have extra pieces of DOM elements laying unnecessarily here and there in the page.
What’s more important is the flexibility provided by solution 2, what if you have to change the underlying markup on different screens in the future? (ex. hide some columns in smaller screens)
I try to build a web application that will fit with almost all sizes of devices/browsers. To do so, my actual approach is to define,inside of my body, a div that will take the whole space of body:
#mydiv {
width: 100%;
height: 100%;
}
I calculate, then, width and height of my available space using:
var Width= $("#mydiv").width();
var Height= $("#mydiv").height();
I do what I want after. I position my elements with jQuery/CSS (percentages, top property, absolute positionnong,...), I draw with Rapahael.js....
I discovered that this approach is not always efficient, especially for browsers that display their addons as HTML. For example in my Chrome, when I install a toolbar addon, this toolbar is rendered in the page code source as HTML elements with their own styles (top=0, fixed postion..). The consequence is that all my work with top position is shifted by the height of the toolbar.
How can I calculate the net height of body?
What are alternative approaches to create webpage that adapts with the net browser size (I mean after any DOM injected elements outside of my control like ask.com toolbar... )?
Edit: so I gave this problem a little thought and I figure that if an add-on is going to draw to the DOM, it's most likely going to append itself to body. So, if you structured your document body in this manner:
<body>
<div id="container">
... all your content here
</div>
</body>
and the add-on inserted itself like this:
<body>
<div id="toolbar" style="margin:0;padding:5px;position:fixed;top:0px;left:0px;width:100%;height:20px;background-color:#000;color:#fff">toolbar</div>
<div id="container">
... all your content here
</div>
</body>
You could overcome this by setting #container's position to relative and adding the following script to your page:
var shift_amount = 0;
var children = document.body.children;
for(var i = 0; i < children.length; i++){
if(children[i].style.position == 'fixed'){
shift_amount += parseFloat(children[i].style.height);
}
}
var Height = $(window).height();
var Width = $(window).width();
if(shift_amount > 0){
// subtract fixed element height from available height
Height -= shift_amount;
}
As I'm pretty sure the question #RoryMcCrossan linked answers the question you asked, I will add that the preferred approach to creating responsive websites is to use media queries to adapt your CSS at various widths (mobile, tablet, desktop). Here is an example of media queries in action using Twitter Bootstrap, open that page and resize your browser window.
<style>
/* target only browsers less than or equal to 600px; */
#media (max-width: 600px) {
.sidebar {
display: none;
}
}
</style>
Regarding the issue of having toolbars and other components rendered in the HTML, this is going to be difficult to overcome as you can't know any and every element that will get injected into the DOM outside of your control. If you are targeting a specific use case, please point us to that add-on and there may be a solution to find.
I would take a look at the "mutation observers" to detect changes in the DOM structure.
Then you can just get those values again.
MutationObserver = window.MutationObserver || window.WebKitMutationObserver;
var observer = new MutationObserver(function(mutations, observer) {
// fired when a mutation occurs
Width= $("#mydiv").width();
Height= $("#mydiv").height();
});
// define what element should be observed by the observer
// and what types of mutations trigger the callback
observer.observe(document, {
subtree: true,
attributes: true
//...
});
I am trying to make a flipbook using turn.js that has the same functionality as the example on the website http://www.turnjs.com/samples/magazine/
When looking at how to achieve this I came across these pages
http://www.turnjs.com/docs/Method:_zoom
http://turnjs.com/docs/How_to_add_zoom_to_turn.js
But after following these instructions on the pages my flipbook works nothing like the sample one.
I tried using the sample provided and breaking it down into sections to get mine working but I have not gotten any closer to solving this problem and the sample contains a bunch of other scripts and I am not sure if they are required for the zoom or are used for other things.
Not sure if I am missing something really simple or if my code is really off but my html looks something like this.
Right now all I get when clicking the zoom button is that the book scales up 150%
Was wondering if anyone could tell me what I am missing to get that zoom?
<div class="row">
<div id="zoom-viewport">
<div id="flipbook">
// wordpress loop
<div class="page">
// page contents
</div>
// end loop
</div>
</div>
</div>
and jQuery
//----------------------------
// Initialize
var _width = $('#flipbook-wrap').width(),
_height = Math.round(70.909090909/100*_width),
_winWidth = $window.width(),
_winHeight = $window.height();
$("#flipbook").turn({
width: _width,
height: _height,
autoCenter: true
});
//----------------------------
// Zoom in button
$('.fullscreen').click(function(e){
e.preventDefault();
$("#flipbook").turn("zoom", 1.5);
});
Your code isn't showing everything (e.g. where ".fullscreen" or the "zoom button" is in your HTML), so my answer may not be precise.
Looking at the sample, you should find the code:
$('.magazine-viewport').zoom('zoomIn', pos);
This seems to differ from turn('zoom', ...), and appears to be undocumented. This is a function that will zoom in the element defined as a turn object. I believe, for you, this is your "#flipbook" element, instead of ".magazine-viewport".
The parameters are "zoomIn" and pos, which may be a different functionality that what you're using currently. The "pos" appears to be a JS object that contains "x" and "y" properties, meant to define where you clicked on the magazine. These coordinates are relative to the magazine, not the whole screen, so keep that in mind.
So, I think you need something like this (at least try it at a starting point):
$('#flipbook').click(function(e) {
var pos = {
x: e.pageX - $(this).offset().left,
y: e.pageY - $(this).offset().top
};
$('#flipbook').zoom('zoomIn', pos);
});
Hope this helps!
To get zoom to work with turn.js, there are three things you need to do:
Setup the proper dom structure, zoom won't work without the "container" div to wrap the flipbook.
<div class="magazine-viewport">
<div class="container">
<div class='magazine'>
<div id='p1'><img src='book_1.jpg'></div>
<div id='p2'><img src='book_2.jpg'></div>
</div>
</div>
</div>
Setup the js events
$( document ).ready(function() {
//Initialize the turn.js flipbook
$('.magazine').turn({
width: 1136,
height:734,
pages:100,
autoCenter: false,
when:{
missing: function (e, pages) {
for (var i = 0; i < pages.length; i++) {
$('.magazine').turn('addPage',page[pages[i]],pages[i]);
}
}
}
});
//Initialize the zoom viewport
$('.magazine-viewport').zoom({
flipbook: $('.magazine')
});
//Binds the single tap event to the zoom function
$('.magazine-viewport').bind('zoom.tap', zoomTo);
//Optional, calls the resize function when the window changes, useful when viewing on tablet or mobile phones
$(window).resize(function() {
resizeViewport();
}).bind('orientationchange', function() {
resizeViewport();
});
//Must be called initially to setup the size
resizeViewport();
}
function page(num){
var elem = $('<div />',{}).html('<div><img src="book_'+num+'.jpg></div>');
return elem;
}
function zoomTo(event) {
setTimeout(function() {
if ($('.magazine-viewport').data().regionClicked) {
$('.magazine-viewport').data().regionClicked = false;
} else {
if ($('.magazine-viewport').zoom('value')==1) {
$('.magazine-viewport').zoom('zoomIn', event);
} else {
$('.magazine-viewport').zoom('zoomOut');
}
}
}, 1);
}
function resizeViewport() {
var width = $(window).width(),
height = $(window).height(),
options = $('.magazine').turn('options');
$('.magazine-viewport').css({
width: width,
height: height
}).zoom('resize');
}
Define proper css styles for the elements, the trick here is that the negative coordinates of the magazine class is compensated by the top & left offsets of the container class.
.magazine-viewport .container{
position:absolute;
top:367px;
left:568px;
width:1136px;
height:734px;
margin:auto;
}
.magazine-viewport .magazine{
width:1136px;
height:734px;
left:-568px;
top:-367px;
}
/* Important: the image size must be set to 100%.
* Otherwise the position of the images would be messed up upon zooming.
*/
.magazine img{
width:100%;
height:100%;
}
That should get it to work, if you want to load a larger version of the image upon zooming, take a look at the loadSmallPage() & loadLargePage() functions in the magazine example.
I had the same problem, but I decided to just use a third party zoom plugin (Jack Moore's jQuery zoom). It turns out the example in the site is a lot more complicated, with a json to create diferent regions and images for each paragraph.
It really depends on what you're using turn.js for, but I think the documentation isn't right, or the software itself is missing something. Either way, I do suggest you look into using some other solution for the problem.
turn.js provides an example with zoom. The difficulty to make it work is to gather all the required files. But if you watch the code, it is possible. Say the root is magazine, it goes two folders up to get lib and extras folders where java scripts are laying. In addition, you have to add the "default" and large pages in the pages folder. When you get the sample, there are only the thumbnails in. Say for 1-thumb.jpg, you have to add 1.jpg and 1-large.jpg
There is a very usefull Firefox plugin to get them : CacheViewer.
I have managed to do it with my book, and reorganize the paths in the code to have something cleaner: put lib and extras at the same level than pages. A recursive grep for "/../../" will give you all the locations in html and js code.
Is there a simple and reliable solution for detecting window vertical scrollbar appears/disappears?
window.onresize isn't triggered when after JavaScript DOM manipulation page becomes high enough for appearing scrollbar.
In this very similar post Detect if a page has a vertical scrollbar described solution how to detect whether scrollbar is present or not, but I need to know when exactly it appears.
Sorry to bring this back from the dead but I have just run in to this limitation and came up with my own solution. It's a bit hacky but stick with me ...
The idea is to add a 100% width invisible iframe to the page and listen for resize events on it's internal window. These events will pick up changes not only to the outer window's size but also when scrollbars get added to or removed from the outer window.
It triggers a regular window resize event so it requires no extra code if you are already listening for window resize.
Tested in IE9 and Chrome/Firefox latest - could maybe be made to work in older IEs but my project doesn't support those so I haven't tried.
https://gist.github.com/OrganicPanda/8222636
Based on OrganicPanda's answer, came up with this jquery thing
$('<iframe id="scrollbar-listener"/>').css({
'position' : 'fixed',
'width' : '100%',
'height' : 0,
'bottom' : 0,
'border' : 0,
'background-color' : 'transparent'
}).on('load',function() {
var vsb = (document.body.scrollHeight > document.body.clientHeight);
var timer = null;
this.contentWindow.addEventListener('resize', function() {
clearTimeout(timer);
timer = setTimeout(function() {
var vsbnew = (document.body.scrollHeight > document.body.clientHeight);
if (vsbnew) {
if (!vsb) {
$(top.window).trigger('scrollbar',[true]);
vsb=true;
}
} else {
if (vsb) {
$(top.window).trigger('scrollbar',[false]);
vsb=false;
}
}
}, 100);
});
}).appendTo('body');
This will trigger 'scrollbar' events on the window, if they appear/dissapear
Works on chrome/mac, at least. now, someone extend this to detect horizontal scrollbars :-)
The Scoop
It is possible to detect changes in scrollbar visibility by using ResizeObserver to check for changes in the size of the element that may take scrollbars and changes in the size of its contents.
Rationale
I started implementing a solution with the <iframe> method but quickly found that having a complete implementation required breaking the separation of concerns among the views of my application. I have a parent view which needs to know when a child view acquires a vertical scrollbar. (I don't care about the horizontal scrollbar.) I have two situations that may affect the visibility of the vertical scrollbar:
The parent view is resized. This is under direct control of the user.
The child view's contents becomes bigger or smaller. This is under indirect control of the user. The child view is showing the results of a search. The quantity and type of results determine the size of the child view.
I found that if I used <iframe> I'd have to muck with the child view to support the parent's needs. I prefer the child to not contain code for something which is purely a concern of the parent. With the solution I describe here, only the parent view needed to be modified.
So in looking for a better solution, I found this answer by Daniel Herr. He suggests using ResizeObserver to detect when a div's dimensions change. ResizeObserver is not yet available natively across browsers but there is a robust ponyfill/polyfill that I use for support in cases where native support is not available. (Here is the spec for ResizeObserver.)
Proof-of-Concept
I use this polyfill in its ponyfill mode. That way, the global environment remains untouched. This implementation relies on window.requestAnimationFrame, and will fall back on setTimeout for platforms that don't support window.requestAnimationFrame. Looking at the support for requestAnimationFrame on "Can I use...?", what I see there does not bother me. YMMV.
I have a live proof-of-concept. The key is to listen to changes in size on the DOM element that can accept scroll bars (the element with id container, in green) and listen to changes in size on the content that may need scrolling (the element with id content). The proof-of-concept uses interact.js to manage a resizer element (with id resizer, in blue) that allows resizing container. If you drag the bottom right corner of resizer, it will resize both resizer and container. The two buttons allow simulating changes in the size of the contents displayed by container.
I'm using this method in code that is currently at a pre-release stage, meaning it passed tests on multiple browsers, and is being evaluated by stakeholders, but is not yet in production.
The HTML:
<!DOCTYPE html>
<html>
<head>
<script data-require="interact.js#*" data-semver="1.0.26" src="//rawgit.com/taye/interact.js/v1.0.26/interact.js"></script>
<script src="//rawgit.com/que-etc/resize-observer-polyfill/master/dist/ResizeObserver.global.js"></script>
<link rel="stylesheet" href="style.css" />
</head>
<body>
<div id="resizer">
<div id="container">
<ul id="content">
<li>Something</li>
</ul>
</div>
</div>
<button id="add">Add to content</button>
<button id="remove">Remove from content</button>
<p>Scroll bar is: <span id="visibility"></span></p>
<ul id="event-log"></ul>
<script src="script.js"></script>
</body>
</html>
The JavaScript:
var container = document.getElementById("container");
var resizer = document.getElementById("resizer");
interact(resizer)
.resizable({
restrict: {
restriction: {
left: 0,
top: 0,
right: window.innerWidth - 10,
bottom: window.innerHeight - 10
}
}
})
.on('resizemove', function(event) {
var target = resizer;
var rect = target.getBoundingClientRect();
var width = rect.width + event.dx;
var height = rect.height + event.dy;
target.style.width = width + 'px';
target.style.height = height + 'px';
});
var content = document.getElementById("content");
var add = document.getElementById("add");
add.addEventListener("click", function() {
content.insertAdjacentHTML("beforeend", "<li>Foo</li>");
});
var remove = document.getElementById("remove");
remove.addEventListener("click", function() {
content.removeChild(content.lastChild);
});
// Here is the code that pertains to the scrollbar visibility
var log = document.getElementById("event-log");
content.addEventListener("scrollbar", function () {
log.insertAdjacentHTML("beforeend", "<li>Scrollbar changed!</li>");
});
var visiblity = document.getElementById("visibility");
var previouslyVisible;
function refreshVisibility() {
var visible = container.scrollHeight > container.clientHeight;
visibility.textContent = visible ? "visible" : "not visible";
if (visible !== previouslyVisible) {
content.dispatchEvent(new Event("scrollbar"));
}
previouslyVisible = visible;
}
// refreshVisibility();
var ro = new ResizeObserver(refreshVisibility);
ro.observe(container);
ro.observe(content);
The CSS:
* {
box-sizing: border-box;
}
#container {
position: relative;
top: 10%;
left: 10%;
height: 80%;
width: 80%;
background: green;
overflow: auto;
}
#resizer {
background: blue;
height: 200px;
width: 200px;
}
If you're using AngularJS, you can use a directive to detect when the width changes (assuming the appearing/disappearing scrollbar is a vertical one):
app.directive('verticalScroll', function($rootScope){
return {
restrict: 'A',
link: function (scope, element) {
scope.$watch(
function() {
return element[0].clientWidth;
},
function() {
$rootScope.$emit('resize');
}
);
}
}
});
This fires an event on the root scope which other directives or controllers can listen for.
The watch is fired by the angular digest loop, so this relies on Angular having loaded/removed the extra content which has caused your scrollbar to appear/disappear.
Dynamically Detect Browser Vertical Scrollbar Event by
comparing window.innerWidth to getBoundingClientRect()
of a DIV element using Javascript. Tested with latest
IE FF Chrome. See documentation here
It's all about when you need to determine the scrollbar's visibility.
The OP speaks of a time "after JavaScript DOM manipulation". If that manipulation happens in your code, then that's the time for checking if the scrollbar is visible. Why do you need an event in addition to that? How is it that you don't know when this DOM manipulation occurs?
I realize this is an old question, but I'm just now dealing with this in a pure javascript project, and I have no issue knowing when to check for scrollbar visibility. Either a user event fires, or a system event fires, and I know when the DOM manipulation occurs because I'm causing it via javascript. I don't see a case where that javascript DOM manipulation is outside of my code's awareness.
Maybe a scrollbarVisibilityChange event would be convenient, but it's certainly not necessary. This strikes me as a non-issue, 9 years later. Am I missing something?
If you only need to detect the scroll appearance on Windows browsers (except IE), here's my solution with Resize Observer API for vertical scroll as an example.
Idea
Append <div> with position: fixed to <body>
Make it 100% width and observe for size changes
The appearance of the scroll reduces the <div>'s width, which in turn calls the observer callback.
Why only Windows browsers?
Mobile and macOS browsers have a disappearing scroll that is taken out of the document flow and doesn't affect the page layout.
Why should the position be fixed and not absolute?
Element with position: fixed is positioned relative to the initial containing block established by the viewport.
position: absolute may fail if the <body> is also absolutely positioned and has a different width than the viewport.
const innerWidthFiller = document.createElement('div')
innerWidthFiller.style.cssText = 'position: fixed; left: 0; right: 0'
document.body.appendChild(innerWidthFiller)
const detectScroll = () => {
const {clientHeight, scrollHeight} = document.documentElement
window.result.value = scrollHeight > clientHeight
}
const resizeObserver = new ResizeObserver(detectScroll)
resizeObserver.observe(innerWidthFiller)
#test {
border: 1px solid;
white-space: nowrap;
}
output {
font-weight: bold;
}
<button onclick="test.style.fontSize='100vh'">Enlarge the text</button>
<button onclick="test.style.fontSize=''">Reset</button>
Page scroll state: <output id="result"></output>
<hr>
<span id="test">Test element</span>
Greetings.
I am developing an animated homepage for a Flash-HTML hybrid website, and for the sake of standards, my solution is proving difficult. I am not a Javascript pro, so any help is appreciated!
Here is the run-down:
For Flash users, HTML page loads a variable-height AS3 Flash movie that will start at 556 pixels high, and after finishing its animation sequence, tween via Actionscript + JavaScript to 250 pixels high.
To kick off this movie sequence -- (below-left) -- I am attempting to set the initial height of the Flash movie via MooTools, so if users do not have Flash or Javascript enabled, they will see the shorter-height image area with alternative image content and HTML content revealed (below-right).
Element.setStyle sets the height just fine until swfObject runs, at which point the movie collapses since I am not specifying a height via CSS. If users do not have Flash, it defaults to the height of a static image.
So here is my question: Does anyone know how to dynamically pass a height variable to swfobject when it is set up to width/height # 100%? Am I killing myself for no reason trying to work with two page heights?
Image Sequence:
Left - Initial Flash movie with HTML navigation below
Right - Resized movie at the end of the sequence with HTML nav & content below, looks the same as no-Flash version (static image)
alt text http://client.deicreative.com/op/images/twopages.jpg
^^ should land here for users w/o Flash
<script type="text/javascript">
<!--
window.addEvent('domready', function() {
$('flashContent').setStyle('height', 556); // sets height for initial movie
$('homeContent').setStyle('display', 'none'); // hides homepage text + photos below
doSwfObject(); // attempting to start swfObject after setStyle is done
});
function resizePage(h) { // to be called from AS3
var tweenObj = new Fx.Tween('flashContent');
tweenObj.start('height', h);
}
function doSwfObject(){
var flashvars = {};
var params = { scale: "noScale" };
var attributes = { id: "flashContent", name: "flashContent" };
swfobject.embedSWF("swf/homeMovie.swf", "flashContent", "100%", "100%", "9.0.0", false, flashvars, params, attributes);
alert(document.getElementById('flashContent').style.height);
// alerts & shows correct height, but page collapses after hitting 'ok'
}
//-->
</script>
The simplest solution is to embed your SWF in a wrapper DIV. Set the SWF to 100% width/height of the wrapper DIV, then use JS to resize the wrapper DIV, not the <object> itself. Less buggy that way.
Since SWFObject 2 replaces the target DIV with the object, you'll need an additional div in your markup:
<div id="wrapper">
<div id="flashcontent"></div>
</div>
becomes
<div id="wrapper">
<object id="flashcontent" width="100%" height="100%" (etc.) ></object>
</div>
I think the act of posting something on here helps me think through the problem -- after doing so, the answer became more clear. So here is my solution for anyone who stumbles across this later.
To animate the Flash movie's height to its initial, taller state while preserving shorter height for non-Flash users (see images above), I use JavaScript the same way I would to tween the movie's height once sequence is complete. The result resembles a push-down ad on a newspaper website.
In AS3, after preloading is done, I tell Javascript to tween the height of the flash movie container (simplified, obviously -- there is no preloading code):
package {
import flash.display.MovieClip;
import flash.display.StageAlign;
import flash.display.StageScaleMode;
import flash.external.ExternalInterface;
public class HomeMovie extends MovieClip {
private var stageHeight:uint;
public function HomeMovie(){
this.stage.scaleMode = StageScaleMode.NO_SCALE;
this.stage.align = StageAlign.TOP_LEFT;
stageHeight = 556;
// Tell javascript the stage needs resizing.
if (ExternalInterface.available) {
ExternalInterface.call("resizePage", stageHeight);
}
}
}
}
In JavaScript (via MooTools):
<script type="text/javascript">
<!--
window.addEvent('domready', function() { // hide content on home-page below movie
$('homeContent').setStyle('display', 'none');
});
function resizePage(h) {
var tweenObj = new Fx.Tween('flashContent', {
property:'height',
duration:500,
transition:Fx.Transitions.Quad.easeOut
});
tweenObj.start(h);
}
//-->
</script>
I will probably take it one step further and check for Flash before hiding the home-page content, so that it will not occur if the user has Javascript but not Flash. Again, this is all for the sake of standards.
Have you tryed SWFForceSize? It's an SWFObject addon and it could help you. Even if you don't use it, you could take a look at the source code to see how they do things.
Btw you don't need SWF object when using Mootools as it has a call called Swiff that does everything SWFObject does and then some! :D