Prototype/Scriptaculous: Selecting groups of paragraphs (<p>) by clicking on them
-
08-07-2019 - |
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!
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>