Question

A list of paragraphs (<p>) is given. As soon as the user clicks on paragraph A the class of paragraph A changes to "activated". Now the user selects paragraph B and all the paragraphs between A and B change their class to "activated".

By clicking on B again, only A remains with the class "active".

By clicking on A the class "active" gets removed on all paragraphs between A and B (including A and B).

It shouldn't be possible to "deactivate" any paragraph between A and B. The selection between A and B should always be a uninterrupted list of selected paragraphs.

Can anyone give me a hint on how to realize this with Prototype/Scriptaculous? The application is implemented in Rails, so any hint in RJS would even be more appreciated!

Was it helpful?

Solution 4

OK, in the meantime and with the help of a coworker I came up with an own answer to this problem:

<script type="text/javascript">
    // holds paragraph A (first selected paragraph)
    var a_selected = null;
    // holds paragraph B (second selected paragraph)
    var b_selected = null;
    // holds all 'active' paragraphs
    var selected_paras = [];

    function class_flipper_init() {
        // reset paragraphs A and B
        a_selected = null;
        b_selected = null;
        var paragraphs = $$("#foobar p");
        paragraphs.each(function(paragraph, index) {
            // if user clicks on a paragraph
            paragraph.observe("click", function(event) {
                // if A and B are 'active': reset everything.
                if(b_selected != null) {
                    selected_paras.each(function(i) {
                        toggleStyle(i);
                    })
                    a_selected = null
                    b_selected = null
                    return
                }
                // if A is 'active'
                if(a_selected != null) {
                    // if A is 'active' and selected B is below A:
                    // select all paragraphs between A and B
                    if(a_selected < index) {
                        b_selected = index;
                        for (var i = a_selected + 1; i <= index; i++ ) {
                            toggleStyle(paragraphs[i])
                        }
                    }
                    // if A is 'active' and selected B is above A: 
                    // select all paragraphs between A and B
                    else if(a_selected > index) {
                        b_selected = index;
                        for (var i = a_selected - 1; i >= index; i-- ) {
                            toggleStyle(paragraphs[i])
                        }
                    }
                    // if A == B
                    else {
                        toggleStyle(paragraph)
                        a_selected = null
                    }
                }
                // if A is selected
                else {
                    a_selected = index;
                    toggleStyle(paragraph)
                }
            });
        });
    }

    function toggleStyle(paragraph) {
        // remove active class
        if (paragraph.hasClassName("active")) {
            paragraph.removeClassName("active");
            selected_paras = selected_paras.without(paragraph)
        } 
        // set active class
        else {
            paragraph.addClassName("active");
            selected_paras.push(paragraph)
        }
    }
</script>

class_flipper_init() is called everytime the page (or in my case a certain partial) is loaded.

Please don't hesitate to submit a solution written in "pure" RJS or something more elegant. :-)

OTHER TIPS

Assuming your paragraphs are in a wrapper div called 'info': (I haven't tested it, but it would be something like this)

$('info').select('P').each(function(element) {
    Event.observe(element,'click',function(event){
        flipIt(event)
    })
})

function flipIt(evt) {  
    var element = evt.element();
    if($(element).hasClassName('active')) {
        $(element).removeClassName('active')
    }
    else {
        $(element).addClassName('active')
    }
}

Try this

<!DOCTYPE HTMP PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
 "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html>
 <head>
  <%= javascript_include_tag :defaults %>
 </head>
 <body>

<style type="text/css">
  .active {
    background-color: maroon;
  }
</style>

<div id="info">
<p>
1 ald fhasdfd sfhjfh afhd fhasjfhjsdah fadfhasd<br/>
fasdhfhsdf ajhajkfh dfhdasjf fhdasf asdf<br/>
asdfh hsdjkhf dhfasdfh asdjfkdhfjkasd<br/>
fsdhf jksdhf sdfjkh asfsdf asdfasdfasdh<br/>
</p>
<p>
2 ald fhasdfd sfhjfh afhd fhasjfhjsdah fadfhasd<br>
fasdhfhsdf ajhajkfh dfhdasjf fhdasf asdf<br/>
asdfh hsdjkhf dhfasdfh asdjfkdhfjkasd<br/>
fsdhf jksdhf sdfjkh asfsdf asdfasdfasdh<br/>
</p>
<p>
3 ald fhasdfd sfhjfh afhd fhasjfhjsdah fadfhasd<br>
fasdhfhsdf ajhajkfh dfhdasjf fhdasf asdf<br/>
asdfh hsdjkhf dhfasdfh asdjfkdhfjkasd<br/>
fsdhf jksdhf sdfjkh asfsdf asdfasdfasdh<br/>
</p>
<p>
4 ald fhasdfd sfhjfh afhd fhasjfhjsdah fadfhasd<br>
fasdhfhsdf ajhajkfh dfhdasjf fhdasf asdf<br/>
asdfh hsdjkhf dhfasdfh asdjfkdhfjkasd<br/>
fsdhf jksdhf sdfjkh asfsdf asdfasdfasdh<br/>
</p>
</div>
<%javascript_tag :defer => 'defer' do -%>
  $('info').select('P').each(function(element) {
    Event.observe(element,'click',function(event){
        flipIt(event)
    })
})

function flipIt(evt) {

    var element = evt.element();
    var all = $('info').select('P');
    var first = -1;
    var last = -1;
    var clicked = 0;
    for ( i=0;i<all.size();i++ ) {
         if( all[i].hasClassName('active') && first == -1   )
           first = i;
         if( all[i].hasClassName('active') && first != i  )
           last = i;
         if ( all[i] == element){
              clicked = i;
          }
    }
   if ( first == clicked && last == -1 ){
        all[clicked].removeClassName('active');
       return;

}
   if ( first  == -1 && last == -1 ) {
        all[clicked].addClassName('active');
        return;
}
   if ( last < clicked  && first != -1 ){
      for (i=first;i<=clicked;i++)
        all[i].addClassName('active');
      return;
   }
   if (last == clicked && first != -1 ) {
      for (i=first+1;i<=clicked;i++)
        all[i].removeClassName('active');
    return; }
}

<%end%>
 </body>
</html>

I've tested the code below and it does what you want, although it's a bit convoluted. The key to it is holding the paragraphs in an array, which is achieved using Prototype's $$ function.

<style type="text/css">
  .activated {
    background-color: yellow;
  }
</style>
.
.
.
<div id="container">
  <p>This is paragraph 1.</p>
  <p>This is paragraph 2.</p>
  <p>This is paragraph 3.</p>
  <p>This is paragraph 4.</p>
  <p>This is paragraph 5.</p>
  <p>This is paragraph 6.</p>
</div>
<script type="text/javascript">
  Event.observe(document, "dom:loaded", function() {
    var paragraphs = $$("#container p");
    paragraphs.each(function(paragraph, index) {
      paragraph.observe("click", function(event) {

        // A clicked; toggle activated class on A
        if (index == 0) {
          toggleStyle(paragraphs[0]);

          // A clicked; remove activated class from A + 1 through to B
          // if present
          for (var i = 1; i <= paragraphs.length; i++) {
            if (paragraphs[i] && paragraphs[i].hasClassName("activated")) {
              paragraphs[i].removeClassName("activated");
            }
          }
        }

        // A + 1 clicked; toggle activated class on A + 1
        if (index > 0 && paragraphs[0].hasClassName("activated")) {
          for (var i = 1; i <= index; i++) {
            toggleStyle(paragraphs[i]);
          }
        }
      });
    });
  });

  function toggleStyle(paragraph) {
    if (paragraph.hasClassName("activated")) {
      paragraph.removeClassName("activated");
    } else {
      paragraph.addClassName("activated");
    }
  }
</script>
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top