Forum Moderators: open

Message Too Old, No Replies

Prototype/Scriptaculous Slider

         

almo136

4:18 pm on Jan 6, 2011 (gmt 0)

10+ Year Member



Hi,

I have the below code which creates a simple slider where the image moves when when you hover over it and moves down when you move the mouse out. The

problem is that if you move the mouse in and then out quickly (before the blind up has finished) the image gets stuck at the top. I would like it so that

the image always moves back down when the mouse isn't hovering over it. I need to achieve this using Prototype/Scriptaculous. Possible?

<html>
<head>
<title>script.aculo.us example</title>


<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/prototype/1.7.0.0/prototype.js"></script>
<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/scriptaculous/1.8.3/scriptaculous.js"></script>


<script type="text/javascript">
function ProductSlide(element){
new Effect.toggle(element,'Blind', {duration:3});
}
</script>

</head>
<body>

<div onmouseover="ProductSlide('myimage');" onmouseout="ProductSlide('myimage');" style="height:102px;width:130px;position:relative;">

<div id="mydescription" style="position:absolute;z-index:-1">
<p>Hello</p>
</div>


<div id="myimage">
<img src="http://www.tutorialspoint.com/images/scriptaculous.gif" alt="script.aculo.us" />
</div>

</div>

</body>
</html>

Fotiman

5:23 pm on Jan 6, 2011 (gmt 0)

WebmasterWorld Senior Member 10+ Year Member Top Contributors Of The Month



Because you can't get the state of what Effect.toggle is doing, I think you'd be better off to explicitly use BlindUp and BlindDown. Here's an example that does what you're looking for by creating a queue for the effects.


var q = [];
function adjustQueue() {
// Remove the first item from the queue
q.splice(0,1);
// see if there are any items remaining in the queue
if (q.length > 0) {
doEffect(q[0].element, q[0].effect);
}
}
function doEffect(element, effect) {
new Effect[effect](element, {
duration:3,
afterFinish: adjustQueue
});
}
function ProductSlide(element, effect){
// Allow at most 2 effects in queue
if (q.length == 0) {
q.push({element:element, effect:effect});
doEffect(element, effect);
}
else if (q.length == 1) {
// An action is in queue / currently running
// queue up another one only if it is different
if (q[0].effect != effect) {
q.push({element:element, effect:effect});
}
}
else {
// An action is currently running, with another queued up.
// If the current effect is the same as the running effect, remove
// the queued up action. Otherwise, replace the queued up action
if (q[0].effect == effect) {
q.splice(1,1);
}
else {
q[1] = {element:element, effect:effect};
}
}
}


Then change your markup to this:

<div onmouseover="ProductSlide('myimage','BlindUp');" onmouseout="ProductSlide('myimage','BlindDown');" style="height:102px;width:130px;position:relative;">

Hope that helps.

almo136

11:04 am on Jan 7, 2011 (gmt 0)

10+ Year Member



Thanks, that worked perfectly. I ran in to a problem though. In production there will be more than one image on the page eg:

<div onmouseover="ProductSlide('myimage','SlideUp');" onmouseout="ProductSlide('myimage','SlideDown');" style="height:102px;width:130px;position:relative;float:left;margin-right:20px;">

<div id="mydescription" style="position:absolute;z-index:-1">
<p>Hello</p>
</div>

<div id="myimage">
<img src="http://www.tutorialspoint.com/images/scriptaculous.gif" alt="script.aculo.us" />
</div>

</div>

<div onmouseover="ProductSlide('myimage2','SlideUp');" onmouseout="ProductSlide('myimage2','SlideDown');" style="height:102px;width:130px;position:relative;float:left;margin-right:20px;">

<div id="mydescription" style="position:absolute;z-index:-1">
<p>Hello</p>
</div>

<div id="myimage2">
<img src="http://www.tutorialspoint.com/images/scriptaculous.gif" alt="script.aculo.us" />
</div>

</div>



With this setup there are problems when I move quickly from one image to the other. Ultimately I am trying to achieve an effect simlar to the images on this theme:
[dgcraft.free.fr...]

Fotiman

2:18 pm on Jan 7, 2011 (gmt 0)

WebmasterWorld Senior Member 10+ Year Member Top Contributors Of The Month



You just need each image to have it's own queue. I've modified my original example. The "q" variable is now an object (acting as a map of element ids to queue arrays). I needed to adjust the ProductSlide method to create the initial queue for an element the first time it's called. The queue for each element is then referenced by q[element] instead of just q. I also needed to modify the adjustQueue method to take the element as a parameter so we know which queue to adjust, and that meant modifying the afterFinish callback method to be an anonymous function that calls adjustQueue with the current element.


var q = {};
function adjustQueue(element) {
// Remove the first item from the queue
q[element].splice(0,1);
// see if there are any items remaining in the queue
if (q[element].length > 0) {
doEffect(q[element][0].element, q[element][0].effect);
}
}
function doEffect(element, effect) {
new Effect[effect](element, {
duration:3,
afterFinish: function () {
adjustQueue(element);
}
});
}
function ProductSlide(element, effect){
// Create a queue for this element if it doesn't exist
if (typeof q[element] === "undefined") {
q[element] = [];
}
// Allow at most 2 effects in queue
if (q[element].length == 0) {
q[element].push({element:element, effect:effect});
doEffect(element, effect);
}
else if (q[element].length == 1) {
// An action is in queue / currently running
// queue up another one only if it is different
if (q[element][0].effect != effect) {
q[element].push({element:element, effect:effect});
}
}
else {
// An action is currently running, with another queued up.
// If the current effect is the same as the running effect, remove
// the queued up action. Otherwise, replace the queued up action
if (q[element][0].effect == effect) {
q[element].splice(1,1);
}
else {
q[element][1] = {element:element, effect:effect};
}
}
}


That should do what you're looking for. Let me know if you have any questions. :)

Fotiman

2:34 pm on Jan 7, 2011 (gmt 0)

WebmasterWorld Senior Member 10+ Year Member Top Contributors Of The Month



Also note... the example you linked to uses jQuery (which really makes this sort of thing MUCH simpler). That example will actually stop an effect rather than waiting for it to complete. You might be able to do something similar with script.aculo.us using effect.cancel(), but it will mean storing the effect and knowing which one is currently executing.

almo136

2:41 pm on Jan 7, 2011 (gmt 0)

10+ Year Member



Excellent! Thanks so much.

Unfortunately I can't use Jquery as the cms this will be implemented on already uses scriptaculous and I don't want to have to load Jquery as well just for this.

almo136

2:59 pm on Jan 7, 2011 (gmt 0)

10+ Year Member



One other question actually. When I implement this the images will be generated dynamically so there won't always be the same number of images.

Therefore targeting the div containing the image by it's id won't be possible. Is there a way to target the 'next image', 'next image with a certain class', '2 div's down' or some other way of targeting the div without specifying an id?

Hope that makes sense.

Fotiman

3:24 pm on Jan 7, 2011 (gmt 0)

WebmasterWorld Senior Member 10+ Year Member Top Contributors Of The Month




some other way of targeting the div without specifying an id?


I don't think so. Looking in the documentation for BlindUp [madrobby.github.com], it looks as though it only takes the id of an element as the first parameter. I tried passing in a DOM node reference to see if that would work and it didn't.