表单脚本
选择框脚本
选择框是通过<select>
和<option>
元素创建的。<select>
元素是在JS中是HTMLSelectElement 类型,提供了额外的属性和方法:
add(newOption, relOption) :向控件中插入新<option>
元素,其位置在相关项(relOption)之前。可以通过new Option(text, value)创建HTMLOptionElement 类型的对象,此外用这个函数来调换两个option元素位置也挺好用的(类似于insertBefore )。multiple :布尔值,表示是否允许多项选择;等价于HTML 中的multiple 特性。options :控件中所有<option>
元素的HTMLCollection。remove(index) :移除给定位置的选项。如果不传参数,则会把整个select移除掉。如果传的不是Number类型的,会通过valueOf或者toString转成Number类型。没有重写的话会返回0? selectedIndex :基于0 的选中项的索引,如果没有选中项,则值为-1。对于支持多选的控件,只保存选中项中第一项 的索引。改变这个值也可以设置选中项 。size :选择框中可见的行数;等价于HTML 中的size 特性。type :”select-one “或”select-multiple “,取决是是否有multiple 特性。 选择框的value 属性由选中项决定(一些自定义标签实现了设置value改变选中项 的功能,但默认是没有这个功能的):
如果没有选中的项,则选择框的value保存空字符。 如果有选中项或多个选中项,取第一个选中项作为参考。如果第一个选中项的value有值,则select的value值将设置与其相同;如果无值,则设置为option的文本值。 针对上面的特点,下面是两个例子:
<!DOCTYPE html>
<html lang ="en" >
<head >
<meta charset ="UTF-8" >
<title > Title</title >
</head >
<body >
<form action ="javascript:alert(1)" >
<select >
<option > 1</option >
<option > 2</option >
<option > 3</option >
<option > 4</option >
<option > 5</option >
<option > 6</option >
<option > 7</option >
<option > 8</option >
<option > 9</option >
<option > 10</option >
</select >
</form >
</input >
<script >
var select = document.forms[0 ].elements[0 ];
console.log(select.type);
var options = select.options;
console.log(options.length);
select.remove(0 );
select.remove({});
select.remove(9 );
select.remove(
{
valueOf:function () {
return 7 ;
}
}
);
select.add(options[1 ], options[0 ]);
</script >
</body >
</html >
在做select的value值实验前,我们来了解一下HTMLOptionElement 类型。每个option都是该类型,该类型有下列额外的属性:
index :当前选项在options集合中的索引。label :等价于label特性。selected :布尔值,表示当前选项是否被选中。将这个属性设置为true 可以选中当前选项。text :选项的文本。value :选项的值(等价于HTML 中的value 特性)。在未指定value 特性的情况下,IE8 会返回空字符串,而IE9+、Safari、Firefox、Chrome 和Opera 则会返回与text 特性相同的值。
<!DOCTYPE html>
<html lang ="en" >
<head >
<meta charset ="UTF-8" >
<title > Title</title >
</head >
<body >
<form action ="javascript:alert(1)" >
<select name ="location" id ="selLocation" >
<option value ="Sunnyvale, CA" > Sunnyvale</option >
<option value ="Los Angeles, CA" > Los Angeles</option >
<option value ="Mountain View, CA" > Mountain View</option >
<option value ="" > China</option >
<option > Australia</option >
</select >
</form >
</input >
<script >
var select = document.forms[0 ].elements[0 ];
select.addEventListener("change" , function (e) {
var select = e.target;
console.log(select.value);
console.log(select.options[select.selectedIndex].value
+ "-----" + select.options[select.selectedIndex].text);
});
</script >
</body >
</html >
------------------------------
Los Angeles, CA
Los Angeles, CA-----Los Angeles
Sunnyvale, CA
Sunnyvale, CA-----Sunnyvale
Mountain View, CA
Mountain View, CA-----Mountain View
-----China //主动设置为"",将返回""
Australia
Australia-----Australia //不设置value特性,可以理解成设置value=text(但是没有改变HTML)
针对上面最后一个特性,我想知道,这个是不是自动设置了value特性所造成的。于是我用getAttribute 去获取option的value特性,结果发现是null。这说明了value属性与text相同,并不是自动设置了value特性。而应该是一种类似呼叫转移 (这个词纯属胡诌)的行为。这个结果也给了我们一个启示:也就是前一篇文章开头提到的:对value 属性所做的修改,不一定会反映在DOM中 。虽然value值与value特性息息相关,但是value值的变化并不都是反映了DOM中的value特性的变化,也可能是text的变化。所以要获取value值,千万不要通过getAttribute 这种方式。 通过常规DOM的方式去获取option元素的value与text的效率也会更低。所以我们在得到HTMLOptionElement 对象后,要多去使用value和text属性而避免再通过getAttribute和innerHTML 去获取value和text特性。
选择选项
选中选项有两种方式,一个是设置select元素的selectedIndex ,另外一种则是设置option元素的selected 。接下来分单选 和多选框 进行讨论。 如果是单选框,那么selectedIndex 的用法是没有疑问的,而selected就有一个问题:如果我设置了多个option的selected都为true,那会如何:
<select name ="location" id ="selLocation" >
<option value ="Sunnyvale, CA" selected ="true" > Sunnyvale</option >
<option value ="Los Angeles, CA" selected="true" > Los Angeles</option >
<option value ="Mountain View, CA" selected="true" > Mountain View</option >
<option value ="" > China</option >
<option > Australia</option >
</select >
<script >
var select = document.forms[0 ].elements[0 ];
console.log(select.options[0 ].selected);
console.log(select.options[1 ].selected);
console.log(select.options[2 ].selected);
</script >
----------------------
无论在何种浏览器中都是选中了第三个,可见这种行为只认最后一个的设置。
如果我单纯通过脚本去设置:结果一模一样
<script >
var select = document.forms[0 ].elements[0 ];
select.options[0 ].selected = true ;
select.options[1 ].selected = true ;
select.options[2 ].selected = true ;
console.log(select.options[0 ].selected);
console.log(select.options[1 ].selected);
console.log(select.options[2 ].selected);
</script >
如果是多选框,那selected可以不用讨论了,可以允许多个被选中,也就可以允许多个为true。但是selectedIndex 的行为就很诡异。选中了多个选项,selectedIndex 只会返回第一个被选中的选项的索引。如果通过脚本改变selectedIndex ,则会只选中那一项,即使之前已经选中了很多项。例子就不举了。
添加选项
可以使用JavaScript 动态创建选项,并将它们添加到选择框中。添加选项的方式有很多,第一种方式就是使用如下所示的DOM 方法。
var newOption = document.createElement ("option" )
newOption.appendChild (document.createTextNode ("Option text" ))
newOption.setAttribute ("value" , "Option value" )
selectbox.appendChild (newOption)
第二种就是之前提到过的使用new Option(“Option text”, “Option value”);创建一个HTMLOptionElement 对象,然后通过appendChild 或者select元素独有的add 方法去添加到DOM中。
var newOption = new Option("Option text" , "Option value" );
selectbox.appendChild(newOption);
selectbox.add(newOption, undefined );
移除选项
除了最常规的DOM的removeChild()方法外,还可以使用select元素特有的remove()方法。这个方法传入一个索引即可使用,前面已经试验过了。还有一种遗留机制:
selectbox.options[0 ] = null ;
如果想要移除所有选项,可以遍历获取options的长度,然后执行长度次数的remove(0) 即可(DOM都是动态的)。
for (var i =0 , len=selectbox. options. length ; i < len; i ++){
selectbox.remove(0 );//千万不要以为是remove(i),否则你会发现只能移除索引为偶数的option
}
移动和重排选项
在DOM 标准出现之前,将一个选择框中的选项移动到另一个选择框中是非常麻烦的。整个过程要涉及从第一个选择框中移除选项,然后以相同的文本和值创建新选项,最后再将新选项添加到第二个选择框中。而使用DOM 的appendChild()方法,就可以将第一个选择框中的选项直接移动到第二个选择框中。我们知道,如果为appendChild()方法 (insertBefore也可以 )传入一个文档中已有的元素,那么就会先从该元素的父节点中移除它,再把它添加到指定的位置。
var selectbox1 = document.getElementById ("selLocations1" )
var selectbox2 = document.getElementById ("selLocations2" )
selectbox2.appendChild (selectbox1.options [0 ])
var selectbox1 = document .getElementById("selLocation1" );
var selectbox2 = document .getElementById("selLocation2" );
selectbox2.add(selectbox1.options[0 ], undefined );
表单序列化
随着Ajax 的出现,表单序列化已经成为一种常见需求(第21 章将讨论Ajax)。在JavaScript 中,可以利用表单字段的type 属性,连同name 和value 属性一起实现对表单的序列化。
对表单字段的名称和值进行URL 编码,使用和号(&)分隔。 不发送禁用的表单字段。 只发送勾选的复选框和单选按钮。 不发送type 为”reset”和”button”的按钮。 多选选择框中的每个选中的值单独一个条目。 在单击提交按钮提交表单的情况下,也会发送提交按钮;否则,不发送提交按钮。也包括type为”image”的<input>
元素。 <select>
元素的值,就是选中的<option>
元素的value 特性的值。如果<option>
元素没有value 特性,则是<option>
元素的文本值。 下面是书中给的序列化的代码:
function serialize (form) {
var parts = [],
field = null ,
i,
len,
j,
optLen,
option,
optValue;
for (i=0 , len=form.elements.length; i < len; i++){
field = form.elements[i];
switch (field.type){
case "select-one" :
case "select-multiple" :
if (field.name.length){
for (j=0 , optLen = field.options.length; j < optLen; j++){
option = field.options[j];
if (option.selected){
optValue = "" ;
if (option.hasAttribute){
optValue = (option.hasAttribute("value" ) ? option.value : option.text);
} else {
optValue = (option.attributes["value" ].specified ? option.value : option.text);
}
parts.push(encodeURIComponent (field.name) + "=" + encodeURIComponent (optValue));
}
}
}
break ;
case undefined :
case "file" :
case "submit" :
case "reset" :
case "button" :
break ;
case "radio" :
case "checkbox" :
if (!field.checked){
break ;
}
default :
if (field.name.length){
parts.push(encodeURIComponent (field.name) + "=" + encodeURIComponent (field.value));
}
}
}
return parts.join("&" );
}
富文本编辑
所谓富文本编辑 就是我们可以在页面中建立一个文本编辑区,我们可以对其中的文本进行样式的设置,然后可以保持原样的将这段文本放置入页面的其他位置。故富文本编辑 ,又称为WYSIWYG (What You See Is What You Get,所见即所得 )。就像博客下面的回复编辑区一样,我们可以改变字体大小或者颜色,提交后就可以保持原样地显示在回复区。 这个功能可以说非常有用,那么该如何实现呢?这一技术的本质,就是在页面中嵌入一个包含空HTML 页面的iframe 。通过设置designMode 属性,这个空白的HTML 页面可以被编辑,而编辑对象则是该页面<body>
元素的HTML 代码。designMode 属性有两个可能的值:”off”(默认值)和”on”。在设置为”on”时(需在页面加载完成后设置,所以一般写在load事件处理程序内部 ),整个文档都会变得可以编辑(显示插入符号),然后就可以像使用字处理软件 一样,通过键盘将文本内容加粗 、变成斜体 ,等等。 此外还可以使用contenteditable 属性,可以把contenteditable 属性应用给页面中的任何元素,然后用户立即就可以编辑该元素。还可以在JS脚本中设置contenteditable 属性,contenteditable 属性有三个可能的值:true” 表示打开、“false” 表示关闭,“inherit” 表示从父元素那里继承(因为可以在contenteditable 元素中创建或删除元素)。但是我实验下来发现这个contenteditable 属性好像不能用,所以这里也不再放例子了。
<div class ="editable" id ="richedit" contenteditable></div >
操作富文本
与富文本编辑器交互的主要方式,就是使用document.execCommand() 。这个方法可以对文档执行预定义的命令,而且可以应用大多数格式。可以为document.execCommand()方法传递3 个参数:要执行的命令名称 、表示浏览器是否应该为当前命令提供用户界面的一个布尔值 (为了兼容,这个参数应该始终为false)和执行命令必须的一个值(如果不需要值,则传递null)。 以下是命令表: 下面是书中给的一个简单的富文本编辑例子:
<!DOCTYPE html>
<html >
<head >
<title > Rich Text Editing Example</title >
</head >
<body >
<form method ="post" action ="javascript:alert('Form submitted!')" >
<div id ="divSimple" >
<input type ="button" value ="Bold" >
<input type ="button" value ="Italic" >
<input type ="button" value ="Underline" >
<input type ="button" value ="Indent" >
<input type ="button" value ="Outdent" >
<input type ="button" value ="Copy" >
<input type ="button" value ="Cut" >
<input type ="button" value ="Paste" >
</div >
<div id ="divComplex" >
<input type ="button" value ="Create Link" id ="btnCreateLink" >
<input type ="button" value ="Change Font Size" id ="btnChangeFontSize" >
<input type ="button" value ="Highlight Text" id ="btnHighlight" >
<input type ="button" value ="Get HTML" id ="btnGetHtml" >
<input type ="button" value ="Get Selected Text" id ="btnGetSelected" >
</div >
<div id ="divQuery" > Is the current selection:
<input type ="button" value ="Bold" >
<input type ="button" value ="Italic" >
<input type ="button" value ="Underline" >
</div >
<iframe name ="richedit" style ="height: 500px; width: 1000px" src ="blank.htm" > </iframe >
<input type ="hidden" name ="comments" value ="" >
<input type ="submit" value ="Submit Form" >
</form >
<script type ="text/javascript" >
(function () {
window.addEventListener("load" , function () {
frames["richedit" ].document.designMode = "on" ;
});
var simple = document.getElementById("divSimple" );
var complex = document.getElementById("divComplex" );
var queryDiv = document.getElementById("divQuery" );
document.forms[0 ].addEventListener("submit" , function (event) {
event.target.elements["comments" ].value = frames["richedit" ].document.body.innerHTML;
});
simple.addEventListener("click" , function (event) {
var target = event.target;
if (target.type == "button" ){
frames["richedit" ].document.execCommand(target.value.toLowerCase(), false , null );
}
});
complex.addEventListener("click" , function (event) {
var target = event.target;
switch (target.id){
case "btnGetHtml" :
alert(frames["richedit" ].document.body.innerHTML);
break ;
case "btnCreateLink" :
var link = prompt("What link?" , "http://www.wrox.com" );
if (link){
frames["richedit" ].document.execCommand("createlink" , false , link);
}
break ;
case "btnChangeFontSize" :
var size = prompt("What size? (1-7)" , "7" );
if (size){
frames["richedit" ].document.execCommand("fontsize" , false , parseInt (size,10 ));
}
break ;
case "btnGetSelected" :
if (frames["richedit" ].getSelection){
alert(frames["richedit" ].getSelection().toString());
} else if (frames["richedit" ].document.selection){
alert(frames["richedit" ].document.selection.createRange().text);
}
break ;
case "btnHighlight" :
if (frames["richedit" ].getSelection){
var selection = frames["richedit" ].getSelection();
var range = selection.getRangeAt(0 );
var span = frames["richedit" ].document.createElement("span" );
span.style.backgroundColor = "yellow" ;
range.surroundContents(span);
} else if (frames["richedit" ].document.selection){
var range = frames["richedit" ].document.selection.createRange();
range.pasteHTML("<span style=\"background-color:yellow\">" + range.htmlText + "</span>" );
}
break ;
}
});
queryDiv.addEventListener("click" , function (event) {
var target = event.target;
if (target.type == "button" ){
alert(frames["richedit" ].document.queryCommandState(target.value.toLowerCase(), false , null ));
}
});
})();
</script >
</body >
</html >