geiger
December 9, 2014, 2:47am
1
I’d like to make the page here into a GET method form in which the user can click on/click off multiple wearables and then click a “compare” submit button. The following page would pull data and display a table of specs. Can I do this with HTML/CSS, or do I also need Javascript? Specifically, my issues are:
How do I turn each <li> item into a button?
How do I make each <li> selectable and deselectable?
How can I highlight each <li> when it is selected?
Thank you.
EDIT
page has since been revised, please see below
ralphm
December 9, 2014, 3:02am
2
Yes, you need some kind of programming language for this. Moved to JS forum.
Not sure I understand the question.
Could you elaborate a little on what you are trying to do.
geiger
December 9, 2014, 1:58pm
4
Sorry if I was unclear. I would like the user to be able to select multiple items and then click a submit button that would direct the user to a new URL containing the item IDs. Each item should highlight when selected and return to its normal state when not selected. I would prefer to be able to control the selected state style with css.
As with any problem, split it into small pieces and start with those.
Do that by adding a CSS class of selected
to anything the user clicks on, then remove it if the user clicks on it again.
Here is a simple example (uses jQuery):
<ul class="myList">
<li>Item 1</li>
<li>Item 2</li>
<li>Item 3</li>
<li>Item 4</li>
<li>Item 5</li>
</ul>
$("ul.myList li").on("click", function(){
if($(this).hasClass("selected")){
$(this).removeClass("selected");
} else {
$(this).addClass("selected");
}
});
Demo
geiger
December 9, 2014, 6:57pm
6
Thanks. I’ll try that. How about the actual form submission action?
Did I understand correctly that you want the ids of the selected items to be submitted to a second page via a form?
Well you could do that by having a form (obviously) and a hidden input, whose value you change programatically every time a user selects or deselects anything.
$("ul.myList li").on("click", function(){
if($(this).hasClass("selected")){
$(this).removeClass("selected");
removeFromForm(this.id);
} else {
$(this).addClass("selected");
addToForm(this.id)
}
});
Each element will need its own unique id.
And you’ll need a hidden element in the form.
Why not have a go at writing addToForm yurself.
You can use data properties to store any values on the hidden element.
geiger
December 9, 2014, 11:10pm
10
The selection snippet worked like a charm. Thank you for that.
I am having some trouble with the form. My experience with javascript is very limited, and I clearly do not know what I’m doing here. Would you mind taking a look at the page again? Thank you.
EDIT
page has since been revised, please see below
geiger
December 10, 2014, 11:34pm
11
Okay, I tried, but I really have no idea what I’m doing. Please assist.[code]
Fitbit Charge $129.95
[/code][code][/code]
Well, building on our previous example, we’ll need a form with a hidden input. We’ll also need to give the elements unique ids:
<form id="myForm">
<ul class="myList">
<li id="item_1">Item 1</li>
<li id="item_2">Item 2</li>
<li id="item_3">Item 3</li>
<li id="item_4">Item 4</li>
<li id="item_5">Item 5</li>
</ul>
<input id="selectedItems" name="selectedItems" type="hidden" data-values>
<button>Submit</button>
</form>
Then, whenever anything is selected or deselected we’ll need to update the value of the hidden input:
function getSelectedItemsAsArray() {
var items = $("#selectedItems").data("values");
if (items === ""){
return []
} else if (items.match(",")){
return items.split(",")
} else{
return [items];
}
}
function updateSelectedItems(itemsArray){
$("#selectedItems").data("values", itemsArray.join());
}
function addToSelectedItems(item){
var items = getSelectedItemsAsArray();
items.push(item);
updateSelectedItems(items);
}
function removeFromSelectedItems(item){
var items = getSelectedItemsAsArray();
var index = items.indexOf(item);
items.splice(index, 1);
updateSelectedItems(items)
}
$("ul.myList li").on("click", function(){
if($(this).hasClass("selected")){
$(this).removeClass("selected");
removeFromSelectedItems(this.id);
} else {
$(this).addClass("selected");
addToSelectedItems(this.id);
}
});
This way, when you submit the form POST['selectedItems']
will contain a comma separated list of the ids of whatever was selected.
Here’s a demo . I added a bit of visual feedback as to what POST['selectedItems']
currently contains and I disabled the submit action. Obviously, you can leave this out of your production code.
geiger
December 11, 2014, 11:23pm
13
Hm. Thank you for your work on this. I still can’t seem to get it to work. I made a few minor adaptions.
http://wearabletechforums.com/pages/compare-wearables2/?vs=
Then the best thing to do is to strip down your page so I don’t have to go trawling through lots of code.
Could you please make a page with just three items or so on it?
Please also remove any other content which is not relevant to the problem.
geiger
December 12, 2014, 12:47pm
15
Sure. http://wearabletechforums.com/comparison-sitepoint.html
[code]
<div class="bigBanner">
<h1>Compare Popular Wearables</h1>
<span>Select wearables below. Then, <input type="submit" value="click here" id="submit-compareThese"> for a side-by-side comparison.</span>
</div>
<div class="sectionMain compare">
<div id="wearablesCats">
<div class="specs-cat"><h3>Compare Fitness Bands</h3></div>
<ul class="wearablesList" id="fitnessBands">
<li id="shine">
<img src="/images/specs-shine.jpg">
<div class="specs"><h2>Misfit Shine</h2><span class="price">$99.99+</span></div>
</li>
<li id="gearFit">
<img src="/images/specs-gearFit.jpg">
<div class="specs"><h2>Samsung Gear Fit</h2><span class="price">$149.99+</span></div>
</li>
</ul>
<div class="specs-cat"><h3>Compare Other Activity Trackers</h3></div>
<ul class="wearablesList" id="otherTrackers">
<li id="one">
<img src="/images/specs-one.jpg">
<div class="specs"><h2>Fitbit One</h2><span class="price">$99.95</span></div>
</li>
<li id="zip">
<img src="/images/specs-zip.jpg">
<div class="specs"><h2>Fitbit Zip</h2><span class="price">$59.95</span></div>
</li>
<li id="upMove">
<img src="/images/specs-upMove.jpg">
<div class="specs"><h2>Jawbone UP Move</h2><span class="price">$49.99</span></div>
</li>
</ul>
</div>
</div>
/* Template */
body.node77 .titleBar, body.node77 .breadcrumb {
display: none;
}
body.node77 .bigBanner span {
display: block;
font-size: 16px;
}
/* Form elements */
#submit-compareThese {
-webkit-appearance: none;
padding: 1px 6px;
font-family: Roboto, 'Helvetica Neue', Arial, sans-serif;
font-weight: bold;
font-size: 13px;
cursor: pointer; cursor: hand;
text-transform: uppercase;
background-color: @xbHeadingColor;
color: #FFF;
border: none;
margin: 0 5px;
}
.submit-compareThese:hover {
background-color: #F4883D;
}
/* Categories */
#wearablesCats {
word-spacing:-.25em; /* hide whitespace nodes in all modern browsers (not for webkit)*/
display: table; /* Webkit Fix */
padding-right: 0px;
}
.specs-cat {
display: block;
background-color: @xbColor1;
padding: 10px;
margin: 0 0 10px 0;
word-spacing: normal;
}
.specs-cat h3 {
display: inline-block;
font-size: 16px;
font-family: Roboto, "Helvetica Neue", Arial, sans-serif;
color: #FFF;
font-weight: bold;
}
/* Items */
.compare ul li {
display: inline-block;
margin: 0 10px 10px 0;
vertical-align: top;
background-color: #FFF;
box-sizing: border-box;
border: 2px solid @xbPrimaryBorder;
cursor: pointer; cursor: hand;
word-spacing:0; /* reset from parent*/
}
@media screen and (min-width: 1151px) { /* Max columns */
.compare #smartwatches li { width: calc(100% / 6 - 11px); }
.compare #fitnessBands li { width: calc(100% / 6 - 11px); }
.compare #otherTrackers li { width: calc(100% / 6 - 11px); }
}
@media screen and (max-width: 1150px) {
.compare ul li { width: calc(100% / 5 - 11px); }
}
@media screen and (max-width: 875px) {
.compare ul li { width: calc(100% / 4 - 11px); }
}
@media screen and (max-width: 700px) {
.compare ul li { width: calc(100% / 3 - 11px); }
}
@media screen and (max-width: 525px) {
.compare ul li { width: calc(100% / 2 - 11px); }
}
.compare ul li:hover {
border-color: @alertBackground;
color: @alertBackground;
}
.compare ul li:active {
border-color: #2D74AF;
color: #2D74AF;
}
.compare ul li.selected {
border-color: @alertBackground;
color: #FFF;
background-color: @alertBackground;
}
.compare ul li.selected:active {
border-color: #2D74AF;
background-color: #2D74AF;
}
.compare ul li img {
width: 100%;
height: auto;
}
/* Item checkmark */
.itemPicture {
position: relative;
}
.checkmark {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
display: none;
text-align: center;
z-index: 2;
}
.compare ul li.selected img {
visibility: hidden;
}
/* Item text */
.compare ul li h2 {
font-size: 140%;
font-face: Roboto, 'Helvetica Neue', Arial, sans-serif;
}
.compare ul li .specs {
margin: 0 10px 5px 10px;
}
.compare ul li .price {
font-size: 120%;
}
/* Tags */
#popularComparisons {
display: block;
font-family: 'Roboto', sans-serif;
font-size: 13px;
}
a.tag-compare {
display: inline-block;
background-color: #7A4CAD;
padding: 3px 6px;
border-radius: 2px;
margin: 4px 4px 0 0;
}
a.tag-compare:hover {
background-color: #8B62B7;
text-decoration: none;
}
.tag-compare span {
color: #FFF;
}
[/code]
Hi,
I was being stupid.
You don’t need to alter the data attribute, you need to order the value of the hidden input itself.
Also, use action=""
, not url=""
and change the method
to POST
.
That said, here’s an example you can copy and paste:
index.html
<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8">
<title>Form demo</title>
<style>
/* Template */
body.node77 .titleBar, body.node77 .breadcrumb {
display: none;
}
body.node77 .bigBanner span {
display: block;
font-size: 16px;
}
/* Form elements */
#submit-compareThese {
-webkit-appearance: none;
padding: 1px 6px;
font-family: Roboto, 'Helvetica Neue', Arial, sans-serif;
font-weight: bold;
font-size: 13px;
cursor: pointer; cursor: hand;
text-transform: uppercase;
background-color: @xbHeadingColor;
color: #FFF;
border: none;
margin: 0 5px;
}
.submit-compareThese:hover {
background-color: #F4883D;
}
/* Categories */
#wearablesCats {
word-spacing:-.25em; /* hide whitespace nodes in all modern browsers (not for webkit)*/
display: table; /* Webkit Fix */
padding-right: 0px;
}
.specs-cat {
display: block;
background-color: @xbColor1;
padding: 10px;
margin: 0 0 10px 0;
word-spacing: normal;
}
.specs-cat h3 {
display: inline-block;
font-size: 16px;
font-family: Roboto, "Helvetica Neue", Arial, sans-serif;
color: #FFF;
font-weight: bold;
}
/* Items */
.compare ul li {
display: inline-block;
margin: 0 10px 10px 0;
vertical-align: top;
background-color: #FFF;
box-sizing: border-box;
border: 2px solid @xbPrimaryBorder;
cursor: pointer; cursor: hand;
word-spacing:0; /* reset from parent*/
}
@media screen and (min-width: 1151px) { /* Max columns */
.compare #smartwatches li { width: calc(100% / 6 - 11px); }
.compare #fitnessBands li { width: calc(100% / 6 - 11px); }
.compare #otherTrackers li { width: calc(100% / 6 - 11px); }
}
@media screen and (max-width: 1150px) {
.compare ul li { width: calc(100% / 5 - 11px); }
}
@media screen and (max-width: 875px) {
.compare ul li { width: calc(100% / 4 - 11px); }
}
@media screen and (max-width: 700px) {
.compare ul li { width: calc(100% / 3 - 11px); }
}
@media screen and (max-width: 525px) {
.compare ul li { width: calc(100% / 2 - 11px); }
}
.compare ul li:hover {
border-color: @alertBackground;
color: @alertBackground;
}
.compare ul li:active {
border-color: #2D74AF;
color: #2D74AF;
}
.compare ul li.selected {
color: #FFF;
background-color: blue;
}
.compare ul li.selected:active {
border-color: #2D74AF;
background-color: #2D74AF;
}
.compare ul li img {
width: 100%;
height: auto;
}
/* Item checkmark */
.itemPicture {
position: relative;
}
.checkmark {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
display: none;
text-align: center;
z-index: 2;
}
.compare ul li.selected img {
/*visibility: hidden;*/
}
/* Item text */
.compare ul li h2 {
font-size: 140%;
font-face: Roboto, 'Helvetica Neue', Arial, sans-serif;
}
.compare ul li .specs {
margin: 0 10px 5px 10px;
}
.compare ul li .price {
font-size: 120%;
}
/* Tags */
#popularComparisons {
display: block;
font-family: 'Roboto', sans-serif;
font-size: 13px;
}
a.tag-compare {
display: inline-block;
background-color: #7A4CAD;
padding: 3px 6px;
border-radius: 2px;
margin: 4px 4px 0 0;
}
a.tag-compare:hover {
background-color: #8B62B7;
text-decoration: none;
}
.tag-compare span {
color: #FFF;
}
</style>
</head>
<body>
<form id="compareThese" name="compareThese" action="submit.php" method="POST">
<input id="vs" name="vs" type="hidden" value="" />
<div class="bigBanner">
<h1>Compare Popular Wearables</h1>
<span>Select wearables below. Then, <input type="submit" value="click here" id="submit-compareThese"> for a side-by-side comparison.</span>
</div>
<div class="sectionMain compare">
<div id="wearablesCats">
<div class="specs-cat"><h3>Compare Fitness Bands</h3></div>
<ul class="wearablesList" id="fitnessBands">
<li id="shine">
<img src="http://wearabletechforums.com/images/specs-shine.jpg">
<div class="specs"><h2>Misfit Shine</h2><span class="price">$99.99+</span></div>
</li>
<li id="gearFit">
<img src="http://wearabletechforums.com/images/specs-gearFit.jpg">
<div class="specs"><h2>Samsung Gear Fit</h2><span class="price">$149.99+</span></div>
</li>
</ul>
<div class="specs-cat"><h3>Compare Other Activity Trackers</h3></div>
<ul class="wearablesList" id="otherTrackers">
<li id="one">
<img src="http://wearabletechforums.com/images/specs-one.jpg">
<div class="specs"><h2>Fitbit One</h2><span class="price">$99.95</span></div>
</li>
<li id="zip">
<img src="http://wearabletechforums.com/images/specs-zip.jpg">
<div class="specs"><h2>Fitbit Zip</h2><span class="price">$59.95</span></div>
</li>
<li id="upMove">
<img src="http://wearabletechforums.com/images/specs-upMove.jpg">
<div class="specs"><h2>Jawbone UP Move</h2><span class="price">$49.99</span></div>
</li>
</ul>
</div>
</div>
</form>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.js"></script>
<script>
function getSelectedItemsAsArray() {
var items = $("#vs").val();
if (items === ""){
return []
} else if (items.match(",")) {
return items.split(",")
} else {
return [items];
}
}
function updateSelectedItems(itemsArray) {
$("#vs").val(itemsArray.join());
}
function addToSelectedItems(item) {
var items = getSelectedItemsAsArray();
items.push(item);
updateSelectedItems(items);
}
function removeFromSelectedItems(item) {
var items = getSelectedItemsAsArray();
var index = items.indexOf(item);
items.splice(index, 1);
updateSelectedItems(items)
}
$("ul.wearablesList li").on("click", function() {
if($(this).hasClass("selected")){
$(this).removeClass("selected");
removeFromSelectedItems(this.id);
} else {
$(this).addClass("selected");
addToSelectedItems(this.id);
}
});
</script>
</body>
</html>
submit.php
<?php
print_r($_POST);
geiger
December 12, 2014, 7:38pm
17
I will try this tonight. Thank you. Also, I wish to use the GET method. Are you merely suggesting I use POST or saying this will not work unless I use POST? If the latter, I will need to modify the code so that GET is possible. Thanks.
No, GET will work, too.
It might actually make more sense, as that way people can bookmark comparisons.
geiger
December 12, 2014, 10:54pm
19
It worked! Thank you very much. Is this fully compatible with all modern browsers?
Also, the URL string is ?vs=watch%2Cgear2Neo%2Cpebble%2CgearS%2Cactivite
Is this correct for an array?
One more thing. Sorry about this. Is it possible to sort the items alphabetically? For analytics purposes, it would be important that the URL be the same whenever the selections are the same—even when they are selected in a different order.
geiger
December 12, 2014, 11:48pm
20
Ok, we have a bug. I clicked a DIV multiple times on and off, and ended up with duplicates (peak):
?vs=peak%2Csurge%2Cpeak%2Cwatch