I'm working on a new project now (Picard FTW - Engage!) and it involves converting some existing JavaScript. The "old" JavaScript works fine, but is very difficult to get to and also suffers from the fact that it isn't jQuery. That's a huge problem if you ask me. So as I work through the project I'm slowly converting various dynamic elements into ColdFusion. One of those elements was pretty interesting. A list of fields had an associated piece of metadata. Each piece was represented in simple text. Next to it was an edit link. Clicking edit changed the plain text into a drop down. I reworked this into jQuery and this is what I came up.
First let's define the data. My form will have a list of movies. For each movie there is a simple rating system based on 4 values: Hated It, Disliked It, Liked It, Loved It. It is assumed that the user has already selected some values which end up being rendered as hidden form fields:
star wars: <span class="changeable"><input type="hidden" name="starwars" value="4">Loved It - <a href="" class="edit">edit</a></span><br/>
star trek: <span class="changeable"><input type="hidden" name="startrek" value="3">Liked It - <a href="" class="edit">edit</a></span><br/>
This wouldn't normally be hard coded - it would come in dynamically via ColdFusion. But you get the idea. Notice that I've wrapped the hidden field, the current text, and a link within a span. Ok, now for the jQuery:
$(document).ready(function() {
//used for our drop downs
var values = [1,2,3,4]
var labels = ["Hated It","Disliked It","Liked It","Loved It"]
$(".changeable a.edit").click(function() {
//find the hidden item
var hiddenItem = $("input:hidden", $(this).parent())
//get the current val
var currentVal = hiddenItem.val()
//get the name
var currentName = hiddenItem.attr("name");
//now we can draw our drop down and select the right val
var s = "<select name=""+currentName+"">";
//hard coded values for our drop down
for(var i=0;i<values.length;i++) {
s+= "<option value="" + values[i] + """
if(currentVal == values[i]) s+= " selected"
s+= ">" + labels[i] + "</option>"
}
s += "</select>"
//now replace
$(this).parent().html(s)
return false
})
})
First notice I've got two hard coded values. These represent the values and labels for the drop down I'll build later. Again, this would probably be dynamic. Don't forget ColdFusion provides a nice utility function, toScript, to make it easy to convert ColdFusion variables into JavaScript.
Now let's walk through the main function. My selector looks for links with the class edit with DOM items with the class changeable. I've binded to the click event for the link. This matches the link within the span I used. But I need to get information from the rest of the span. So I grab a pointer to the hidden form field by doing a selector against the parent of the link. Does that make sense? Ie: "jQuery, within the parent of what you just found please look for a hidden input field." Once I have that I can get the value as well as the name.
Once I have the name, and the current value, building the drop down is just a matter of string builder. I use the values/labels variables and just create the select. When done I can replace the link for the span. Remember that $(this) represents the original link so $(this).parent() will be the span.
You can see a demo of this here: http://www.coldfusionjedi.com/demos/changedropdown/test.cfm
And here is the complete script:
<html>
<head>
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1/jquery.min.js"></script>
<script>
$(document).ready(function() {
//used for our drop downs
var values = [1,2,3,4]
var labels = ["Hated It","Disliked It","Liked It","Loved It"]
$(".changeable a.edit").click(function() {
//find the hidden item
var hiddenItem = $("input:hidden", $(this).parent())
//get the current val
var currentVal = hiddenItem.val()
//get the name
var currentName = hiddenItem.attr("name");
//now we can draw our drop down and select the right val
var s = "<select name=""+currentName+"">";
//hard coded values for our drop down
for(var i=0;i<values.length;i++) {
s+= "<option value="" + values[i] + """
if(currentVal == values[i]) s+= " selected"
s+= ">" + labels[i] + "</option>"
}
s += "</select>"
//now replace
$(this).parent().html(s)
return false
})
})
</script>
</head>
<body>
<form method="post">
star wars: <span class="changeable"><input type="hidden" name="starwars" value="4">Loved It - <a href="" class="edit">edit</a></span><br/>
star trek: <span class="changeable"><input type="hidden" name="startrek" value="3">Liked It - <a href="" class="edit">edit</a></span><br/>
<input type="submit" value="click me like you mean it">
</form>
<cfif not structIsEmpty(form)>
<cfdump var="#form#" label="form">
</cfif>
</body>
</html>
Archived Comments
@Ray,
the struct dump returns the right results but your label never changes, is that a feature, or not the point of the excersise?
Not a point. ;) If you look at the complete code, you can see it is always hard coded on initial load.
While I know this is just an example exercise, there are many different ways to accomplish the same thing. A while back I wrote a jQuery plug-in called Linkselect (http://www.givainc.com/labs... that converts a select element into a text link.
This has the benefit of being completely unobtrusive, but adds flexibility that a normally select box doesn't offer (such as the ability for the text to wrap lines.)
Also, on a note specific to your example, you might want to change the line:
$(this).parent().html(s)
To:
$(this).parent().html(s).find("select").focus();
So that the select element gets focus--which would improve keyboard usage quite a bit (since the select element would retain the focus that the anchor used to have.)
Ah, focus - I always forget that. Good one.
Ray,
I realize there are 100 ways to do anything, and as a jQuery newb I am still trying to figure out if the way I accomplished something was a hack job or an elegant solution (it's not always easy to tell the two apart).
I had a similar situation for an "in-line edit", but went about it a little differently. In my form, instead of piecing together strings in JS to create the SELECT, I created the entire SELECT box (options and all) within the form in HTML, but gave it a class of 'hidden' (defined in CSS), thereby hiding it. When 'edit' was clicked, I simply removed the hidden class from the SELECT, and added the 'hidden' class to the 'edit' link.
Any comments, good and bad, about that solution?
Also, I'd love to see more real-world jQuery examples when dealing with dynamic/database driven data. Static examples are great, but nothing I build is static. I often question myself on how to deal with situations where both the jQuery code and the resultant HTML depend on dynamic data sets. I often result in looping over the data set twice, once for JS and the other for HTML, but I'm sure there are more elegant ways to pull ID's and values from the HTML in jQuery.
@Mike: Your way isn't bad at all. And don't forget, jQuery has a "clone" command. You could create ONE select, and then clone it each time you need a new one.
Point taken on the dynamic part here - but I assume most readers would know how to dynamically output simple form values. I also like to focus on the item at hand and not distract people from the main point of the blog entry. Know what I mean?
Is there a way to reverse the function, so that when 'saved' the visible selects can be removed and the value attributed to the hidden fields.
Interesting! You would need to write code to listen for the onchange of the selects and change em back. I'll work up a follow up to this in a new blog entry. It may be a day or two though - kinda packed - short answer is though - it IS possible.
Thanks for your response, I look forward to seeing the answer.
Your current script only accommodates select menus, but I made some changes yesterday to allow text and textarea too by looking at the class of the hidden field (that's all I need right now, but it could take any input). Plus it also gets the contents of the select menus from a php script to make each one unique and more dynamic:
$(".changeable a.edit").click(function() {
var hiddenItem = $("input:hidden", $(this).parent())
var currentVal = hiddenItem.val()
var currentName = hiddenItem.attr("name");
var currentType = hiddenItem.attr("class");
if(currentType == "textarea"){
var s = "<textarea name=\""+currentName+"\" >"+currentVal+"</textarea>";
}
else if(currentType == "select"){
var s = "<select name=\""+currentName+"\" id=\""+currentName+"\">";
s += "</select>";
$.post("select.php", {selectmenu: currentName, currentitem: currentVal},
function(data){
$(data).appendTo('#'+currentName);
});
}
else {
var s = "<input type=\"text\" name=\""+currentName+"\" value=\""+currentVal+"\" />";
}
$(this).parent().html(s);
return false
});
<input type="hidden" name="tacktype" value="Saddle" class="select" />
<input type="hidden" class="textarea" name="features" value="text here" />
Posted:
http://www.coldfusionjedi.c...
Good day! I tried to work, but when an item linkselect submits the form does not contain the value of a select. In what may be the problem?
Who are facing? Can I use linkselect to submit a form?
linkselect of http://www.givainc.com/labs... (my blog http://jdrupal.ru/node/15)
So what _does_ the form contain when you submit?