views:

1856

answers:

3

I'm trying to "single source" a form page which can be in edit mode or view mode. For various reasons, this isn't using the ASP.Net FormView or DetailsView controls.

Since there is no way to disable a textbox without turning its contents gray (well, we could "eat" all of the keystrokes into it, but that isn't very elegant either) and disabling a dropdown list or listbox isn't what we want, our first try was to duplicate all of the form input controls with a label and use CSS to select which ones are visible depending on the mode of the form. That works, but it's ugly to edit and the code-behind has to populate both controls every time.

We could control the visibility in the code-behind to avoid filling both controls, but we still have to add them both to the form.

So I had the idea to use jQuery to swap out the input controls for <label>, <div>, or <span> elements. This works, to some extent, by creating the appropriate selectors and using the replace() jQuery method to swap out the elements dynamically.

The problem is that I not only need to copy the contents, but also the styles, attributes, and sizing of the original input controls (at this point we're only talking about textboxes - we have a different solution for dropdown lists and listboxes).

Brute force should work - "backup" all of the attributes of the input control, create the new "read only" element, then replace the input control with the new element. What I'm looking for is something simpler.

Succinctly, using jQuery, what is the best way to replace a textbox with a label and have the label have the same contents and appear in the same location and style as the textbox?

Here is what I have so far:

$(":text").each( function() {
    var oldClass = $(this).attr("class");
    var oldId = $(this).attr("id");
    var oldHeight = $(this).outerHeight();
    var oldWidth = $(this).outerWidth();
    var oldStyle = $(this).attr("style");
    $(this).replaceWith("<div id='" + oldId + "'>" + $(this).val() + "</div>");
    $("div#" + oldId).attr("class", oldClass);
    $("div#" + oldId).attr("style", oldStyle);
    $("div#" + oldId).width(oldWidth);
    $("div#" + oldId).height(oldHeight);
    $("div#" + oldId).css("display", "inline-block");
});
+1  A: 

When I've done this, I've had to "eat" each keystroke as you describe and mirror it into a "hidden" span tag that mirrors the <input or <select element. The span tags all have a css class that's styled using media selectors to only show for print, and the inputs have a css class that's styled to only show for the screen.

If you don't mind the static width that doesn't scale to the size of the text, you could also just style your <input tags to not show a border for print, but that's pretty ugly.

Joel Coehoorn
+1  A: 

Why not use an edit in place plugin like Jeditable. This way you can generate your view mode and have each field editable at the click of a button.

Chris MacDonald
That's really nice and I'll keep it in mind, but it's a little too much to tackle with ASP.Net controls. I'll definitely experiment with it...
CMPalmer
+4  A: 

This may not suit your needs, but it's a possibility.

<input> and <textarea> tags support the read-only property. The behavior of read-only fields is slightly different than disabled. Here's what the HTML 4.01 Recommendation says:

When set, the readonly attribute has the following effects on an element:

Read-only elements receive focus but cannot be modified by the user.
Read-only elements are included in tabbing navigation.
Read-only elements may be successful. ("Successful" means it will be submitted as a parameter.)

Another key difference is that elements with this attribute can be styled however you like. (You could remove or change the borders and background for instance.) So instead of having to create new elements and copy attributes, you could merely add or remove the read-only attribute.

You could then create a style for these fields "input[readonly] {}". Noting of course that popular versions of IE ignore the attribute selector in CSS. (So maybe just define a class that you add and remove.)

Benry
Fix typo; *readonly (last paragraph). +1 for having the same idea as me. =]
strager
OK, big "duh" from me. I think that works fine (although my jQuery code works pretty well).
CMPalmer
SO to the rescue again! This answer helped me solve the same problem, thanks!
Kurt Schindler