Javascript Slideshow with script.aculo.us

836 views Asked by At

I am making a slideshow on my mvc php site using script.aculo.us to animate it. The model looks up the images from the database, fills it into the view and then the javascript uses script.aculo.us to animate it. However, when it gets to the second slide, it stops working and I can find this error message in Chrome's console:

Uncaught TypeError: Cannot call method 'getStyle' of undefined

I cannot work out why it isn't working. Here is the code. Any ideas?

PHP:

<div id="main">
<div id="mainContainer">
    <div id="showcaseContent">
<?php $i = 0; foreach($showcase as $ShowcaseItems): ?>
        <div id="showcaseSlide<?php echo $i; ?>" <?php if($i != 0){ echo 'style="display: none;"'; }else{ echo 'style="display: block;"'; } ?> >
            <img src="<?php echo $ShowcaseItems['showcase_image']; ?>" width="720px" height="275px" />
        </div>
<?php ++$i; endforeach ?>
    </div>
</div>

Javascript (I have to use the top 4 loop because the client can put from 2-10 images onto the showcase slideshow):

var slides = new Array();

for(i=0; i<=10; i++){
    slides[i] = "showcaseSlide"+i;
    if(document.getElementById(slides[i]) == null) break;
};

var wait = 5000;

function startShowcase(){
    setInterval(showcase(), wait);
};

function showcase(){
    var i = 0;
    Effect.Fade(slides[i], {duration: 1.0, from: 1.0, to: 0.0});
    i++
    if(i == slides.count - 1) {i=0};
    Effect.Appear(slides[i], {duration: 1.0, from: 0.0, to: 1.0});

};

Edit:

The error appears on line 544 of the effects.js script that comes with script.aculo.us, which (as far as I can work out) is the line which starts 'from:' in the following code:

Effect.Appear = function(element) {
  element = $(element);
  var options = Object.extend({
  from: (element.getStyle('display') == 'none' ? 0.0 : element.getOpacity() || 0.0),
Uncaught TypeError: Cannot call method 'getStyle' of undefined
  to:   1.0,
  // force Safari to render floated elements properly
  afterFinishInternal: function(effect) {
    effect.element.forceRerendering();
  },
  beforeSetup: function(effect) {
    effect.element.setOpacity(effect.options.from).show();
  }}, arguments[1] || { });
  return new Effect.Opacity(element,options);
};

And here are the relevant sections of the rendered page:

<!DOCTYPE html>
<head>

<!--Title-->
<title>Urban Feather | Home</title>

<!--CSS Styles-->
<link rel="stylesheet" href='http://www.example.com/2012/styles/layout.css' type="text/css" media="screen, projection" />
<link rel="stylesheet" href='http://www.example.com/2012/styles/forms.css' type="text/css" media="screen, projection" />
<link rel="stylesheet" href='http://www.example.com/2012/styles/fonts.css' type="text/css" media="screen, projection" />

<!--Javascript Scripts-->
<script src="http://www.example.com/2012/scripts/sonOfSuckerfish.js" type="text/javascript"></script>
<script src="http://www.example.com/2012/scripts/script.aculo.us/prototype.js" type="text/javascript"></script>
<script src="http://www.example.com/2012/scripts/script.aculo.us/scriptaculous.js?load=effects" type="text/javascript"></script>
<script src="http://www.example.com/2012/scripts/selectreplace.js" type="text/javascript"></script>
<script src="http://www.example.com/2012/scripts/showcase.js" type="text/javascript"></script>
<script src="http://twitter.com/javascripts/blogger.js" type="text/javascript"></script>

</head>

<div id="main">
    <div id="mainContainer">
        <div id="showcaseContent">
            <div id="showcaseSlide0" style="display: block;" >
                <img src="http://www.example.com/2012/images/showcase/client3.jpg" width="720px" height="275px" />
            </div>
            <div id="showcaseSlide1" style="display: block;" >
                <img src="http://www.example.com/2012/images/showcase/client3.jpg" width="720px" height="275px" />
            </div>
            <div id="showcaseSlide2" style="display: block;" >
                <img src="http://www.example.com/2012/images/client3.jpg" width="720px" height="275px" />
            </div>
        </div>
2

There are 2 answers

7
stormdrain On

http://prototypejs.org/api/element/getStyle

Safari returns null for any non-inline property if the element is hidden (has display set to 'none').

<?php if($i != 0){ echo 'style="display: none;"'; }else...

function showcase(){

    var i = 0;

    Effect.Fade(slides[i], {duration: 1.0, from: 1.0, to: 0.0});
    //slide[0] is the only slide without display:none, 
    //so the first slide (slide[0]) works (Fades)! 
    //Once var i increments, everything is display:none and the
    //getStyle used in prototypes Effect.Appear method doesn't work

    i++

    if(i == slides.count - 1) {i=0};
    Effect.Appear(slides[i], {duration: 1.0, from: 0.0, to: 1.0});
    //slides[1] has display:none so getStyle in Appear breaks

};

that's my guess anyway...


Update:

Think it's an issue with the counter:

var i = 0;
Effect.Fade(slides[i], {duration: 1.0, from: 1.0, to: 0.0});
i++
if(i == slides.count - 1) {i=0};
Effect.Appear(slides[i], {duration: 1.0, from: 0.0, to: 1.0});

i will always be 0 when showcase() is called by the interval, so on the second interval, it is trying to make slides[0] Effect.Fade which was already faded on the first.

This works:

var slides = new Array();
var count = 0;
for(i=0; i<=10; i++){
    slides[i] = "showcaseSlide"+i;
    if(document.getElementById(slides[i]) == null) break;
};

var wait = 1000;//shortened because I'm impatient
function startShowcase(){
    setInterval(showcase, wait);
};

function showcase(){
    if(count==slides.length-1){
        count=0;
    }
    if($(slides[count]).style.display==='none'){
        Effect.Appear(slides[count], {duration: 1.0, from: 0.0, to: 1.0});
    }else{
        Effect.Fade(slides[count], {duration: 1.0, from: 1.0, to: 0.0});
    }
    count++;
}; 

Technically, this is more technical; also proper:

var slides = [], count = 0, wait = 4000;
for(var i in $('showcaseContent').select('div')){slides[i] = "showcaseSlide"+i;};
function startShowcase(){setInterval(showcase, wait);};
function showcase(){
    (count==slides.length)?count=0:count=count;
    ($(slides[count]).style.display==='none')?
        Effect.Appear(slides[count], {duration: 1.0, from: 0.0, to: 1.0}):
        Effect.Fade(slides[count], {duration: 1.0, from: 1.0, to: 0.0});
    count++;
};
startShowcase();

YEAH

0
Phil Young On

Thanks to stormdrain and my other post (break loop based on an element not existing), I have resolved the problem. The loop was not working as it was called before the divs were rendered and the code needed a little restructuring. Here is my working code:

var count = 0; //A loop for the counter
var wait = 4000; //The amount of time (ms) inbetween transitions
var slides = []; //The empty array of slide ids

function startShowcase(){
    for(var i=0; i<10; i++){
        slides[i] = "showcaseSlide"+i;;
        if(!document.getElementById(slides[i])) break;
    };

    setInterval(showcase, wait);
};

function showcase(){
    if(count==slides.length-2){
        count=0;
    }
    if(document.getElementById(slides[count]).style.display==='none'){
        Effect.Appear(slides[count], {duration: 1.0, from: 0.0, to: 1.0});
    }else{
        Effect.Fade(slides[count], {duration: 1.0, from: 1.0, to: 0.0});
    }
    count++;
};