Ian Campbell
Ian Campbell

Reputation: 2748

How can I implement renaming an option in an HTML select?


I want the user to be able to select an option, and to be able to rename this when clicking a "rename" button.

I'm attempting this by nesting an input type = "text" inside the selected option. However, the onblur event in the input is not working...

It seems that select does not allow nesting HTML elements inside option, but it's functionality is ideal... With what structure might I implement something like this, an unordered-list?


Here is the HTML:

<select id = "asdf" size = "2">
    <option>asdf</option>
    <option>qwerty</option>
</select>

<input type = "button" value = "rename" onclick = "rename();" />


...and here is the javascript/jQuery:

function rename() {
    if ($("#asdf").length > 0) {
        var selectedText = $("#asdf").find(":selected").text();
        var selectedIndex = $("#asdf").find(":selected").index();
        var options = document.getElementById("asdf").options;

        options[selectedIndex].innerHTML =
           "<input type = 'text' id = 'field' onblur = 'setField(this.value);'" +
           "value = '" + selectedText + "' />";

        function setField(newName) {
            options[selectedIndex].innerHTML = newName;
        }
    }
}

Upvotes: 3

Views: 2601

Answers (3)

Self Evident
Self Evident

Reputation: 402

I want the user to be able to select an option, and to be able to rename this when clicking a "rename" button.

Unable to find a suitable solution, I wrote the following, which I think works well, and does exactly as mentioned above. It seems a bit lengthy compared to the previous answers, but it uses no libraries.

It uses an <input type=text> for editing the text of a given <option>. The <input> is sized and positioned on top of the select, and hidden when not actually in "edit" mode (when the input does not have focus).

//Javascript

var D = document;
function E(id) { return D.getElementById(id); }


var WIDGET_NAMES = [0,'Orange','Loud','Steel','Sky','Wheat','','Almond','Oak','Water','Vague'];

var $select_widget  = E('ESL_select_widget');
var $widget_name    = E('ESL_widget_name');
var $rename_widget  = E('ESL_rename_widget');
var $delete_widget  = E('ESL_delete_widget');


function Pad10(number) {
	if ((number * 1) < 10) { var pad = "&nbsp; "; } else { var pad = ""; }
	return pad + number;
}//end Pad10()


$select_widget.addEventListener("change", function() {
	$widget_name.value = WIDGET_NAMES[$select_widget.value];
});


$select_widget.addEventListener("keypress", function(event) {
	//Yes, both blur() & focus() are needed...
	if(event.keyCode == 13) {
		D.activeElement.blur();
		$widget_name.style['visibility'] = "visible";
		$widget_name.focus();
	}
});


$widget_name.addEventListener("keypress", function(event) {
	//Yes, both blur() & focus() are needed...
	if(event.keyCode == 13) {
		D.activeElement.blur();
		$select_widget.focus();
	}
});


$widget_name.addEventListener("blur", function(event) {
	$widget_name.style['visibility'] = "hidden";
});


$widget_name.addEventListener("change", function() {
	this.value   = this.value.trim();
	var current_widget = $select_widget.value;
	
	//options count from 0, people count from 1.
	var option = $select_widget.options[current_widget-1];
	option.innerHTML = Pad10(current_widget) + ":: " + $widget_name.value;
	WIDGET_NAMES[current_widget] = $widget_name.value;
});


$rename_widget.addEventListener("click", function() {
	$widget_name.style['visibility'] = 'visible';
	$widget_name.focus();
});


$delete_widget.addEventListener("click", function() {
	var current_widget = $select_widget.value;
	$select_widget.options[current_widget-1].innerHTML = Pad10(current_widget);
	WIDGET_NAMES[current_widget] = '';
	$widget_name.value = '';
});


for (var x = 1; x <= 10; x++) {
	$select_widget.options[x-1].value = x;
	if (WIDGET_NAMES[x] == '') { var mark = ''; } else { var mark = ":: "; }
	$select_widget.options[x-1].innerHTML = Pad10(x) + mark + WIDGET_NAMES[x];
}


$widget_name.value = WIDGET_NAMES[$select_widget.value]

$select_widget.focus();
/* CSS */

* { margin: 0; padding: 0; box-sizing: border-box; }

input.ESL:focus, select.ESL:focus, button.ESL:focus { box-shadow: 0 0 3pt 1pt #006060 }

select.ESL:-moz-focusring { color: transparent; text-shadow: 0 0 0 #000; }

#ESL_container {
	display: inline-block;
	padding: .5rem;
	border : solid 1px black;
}

#ESL_rename_container {
	position: relative;
	display: inline-block;
	padding: 0;
	margin: 0;
	border: 0;
}

#ESL_select_widget {
	width  : 12rem;
	margin : 0 0 .5rem 0;
	padding: 0;
	font-size  : 1rem;
	font-family: arial;
}

#ESL_select_widget option { text-align: left; padding-left: .2rem;}

#ESL_widget_name {
	position: absolute;
	top     : 1px;
	left    : 2.1rem;
	margin  : 0;
	padding : 1px 0 0 0;
	width   : 9.9rem;
	height  : 1.3rem;
	border     : solid 1px transparent;
	text-align : left;
	font-size  : 1rem;
	font-family: arial;  /** set to same as select_widget. **/
	visibility : hidden; /** visible during rename only    **/
}
<!-- HTML -->

<div id=ESL_container>
	<div id=ESL_rename_container>
		<select id=ESL_select_widget class=ESL>
			<option value=1 ></option>
			<option value=2 ></option>
			<option value=3 ></option>
			<option value=4 ></option>
			<option value=5 ></option>
			<option value=6 ></option>
			<option value=7 ></option>
			<option value=8 ></option>
			<option value=9 ></option>
			<option value=10></option>
		</select><br>
		<input id=ESL_widget_name type=text class=ESL value="" tabindex=-1>
	</div><br>
	
	<button type=button id=ESL_rename_widget class=ESL>Rename Widget</button>
	<button type=button id=ESL_delete_widget class=ESL>Delete Widget</button>
</div>

Upvotes: 0

Amol
Amol

Reputation: 1461

You can loop over the options:

for(var i = 0, l = select.options.length; i < l; i++) 
 {
    var option = select.options[i];
    var obj = {};
    if(option.value == "...")
    {
        option.innerHTML = "...";
        obj[option.value] = option.innerHTML;
    }
 }
 return obj;   

I would put that in a function and pass a value you search and the replacement, or a map. Something like:

setOptionText(select, 'value', 'text');
// or
setOptionText(select, {'value': 'text', 'value': 'text', ...});

Reference: HTMLSelectElement

Upvotes: 1

Ian Campbell
Ian Campbell

Reputation: 2748


Ok, I got it --
    It turns out I was able to implement this with a ul structure, and some rather nasty jQuery.


CSS:

    #list {
        list-style-type: none;
    }
    .highlighted {
        background-color: #FFFF00;
    }


HTML:

<ul id = "list">
    <li>asdf</li>
    <li>qwerty</li>
<ul>

<input type = "button" value = "rename item" onclick = "renameSearch();" />


jQuery/javascript:

$("#list").on("click focus", "li", function() {
    $(this)
        .addClass("highlighted")
        .siblings().removeClass("highlighted");
});

function renameSearch() {
    $("#list li").each(function(index) {
        if ($(this).hasClass("highlighted")) {
            //alert(index + ": " + $(this).text());
            $(this).html("<input type = 'text' id = 'renameField' value = '" + $(this).text() + "' />");

            $("#renameField").focus();
            $("#renameField").select();

            $("#list li").on("focusout", "#renameField", function() {
                //alert($(this).val());
                $(this).parent().text($(this).val());
            });
        }
    });
} // end of function


jsFiddle  --  http://jsfiddle.net/icampbell2/59YDF/

Upvotes: 0

Related Questions