19.3 选择框编程19.3.1 选项处理19.3.2 添加选项19.3.3 移除选项19.3.4 移动和重排选项19.4 表单序列化19.5 富文本编辑19.5.1 使用contenteditable19.5.2 与富文本交互19.5.3 富文件选择19.5.4 通过表单提交富文本 19.3 选择框编程选择框使用和元素创建。HTMLSelectElement类型在所有表单字段的公共能力之外又提供了以下属性和方法:❑ add(newOption, relOption):在relOption之前向控件中添加新的。❑ multiple:布尔值,表示是否允许多选,等价于HTML的multiple属性。❑ options:控件中所有元素的HTMLCollection。❑ remove(index):移除给定位置的选项。❑ selectedIndex:选中项基于0的索引值,如果没有选中项则为-1。对于允许多选的列表,始终是第一个选项的索引。❑ size:选择框中可见的行数,等价于HTML的size属性。选择框的type属性可能是”select-one”或”select-multiple”,具体取决于multiple属性是否存在。当前选中项根据以下规则决定选择框的value属性:❑ 若没有选中项,则选择框的值是空字符串。❑ 若有一个选中项,且其value属性有值,则选择框的值就是选中项value属性的值。即使value属性的值是空字符串也是如此。❑ 若有一个选中项,且其value属性没有指定值,则选择框的值是该项的文本内容。❑ 若有多个选中项,则选择框的值根据前两条规则取得第一个选中项的值。每个元素在DOM中都由一个HTMLOptionElement对象表示。HTMLOptionElement类型为方便数据存取添加了以下属性:❑ index:选项在options集合中的索引。❑ label:选项的标签,等价于HTML的label属性。❑ selected:布尔值,表示是否选中了当前选项。把这个属性设置为true会选中当前选项。❑ text:选项的文本。❑ value:选项的值(等价于HTML的value属性)。 19.3.1 选项处理只允许选择一项的选择框,获取选项最简单的方式是,使用选择框的selectedIndex属性;允许多选的选择框,selectedIndex属性就像只允许选择一项一样。设置selectedIndex会移除所有选项,只选择指定的项,而获取selectedIndex只会返回选中的第一项的索引。选项还可以通过取得选项的引用并将其selected属性设置为true来选中。 function getSelectedOptions(selecbox) { let result = new Array(); for (let option of selectbox.options) { if (option.selected) { result.push(option); } } return result; } 会返回给定选择框中所有选中项的数组。首先创建一个包含结果的数组,然后通过for循环迭代所有选项,检测每个选项的selected属性。如果选项被选中,就将其添加到result数组。最后是返回选中项数组。 19.3.2 添加选项可使用JavaScript动态创建选项,并将它们添加到选择框。也可使用Option构造函数创建新选项,这个构造函数是DOM出现之前就已经得到浏览器支持的。Option构造函数接收两个参数:text和value,其中value是可选的。虽然这个构造函数通常会创建Object的实例,但DOM合规的浏览器都会返回一个元素。这意味着仍然可以使用appendChild()方法把这样创建的选项添加到选择框。另一种添加新选项的方式是:使用选择框的add()方法。DOM规定这个方法接收两个参数:要添加的新选项,和要添加到其前面的参考选项。如果想在列表末尾添加选项,那么第二个参数应该是null。IE8及更早版本对add()方法的实现稍有不同,其第二个参数是可选的,如果要传入则必须是一个索引值,表示要在其前面添加新选项的选项。DOM合规的浏览器要求必须传入第二个参数,因此在跨浏览器方法中不能只使用一个参数(IE9是符合DOM规范的)。此时,传入undefined作为第二个参数可以保证在所有浏览器中都将选项添加到列表末尾。 19.3.3 移除选项与添加选项类似,移除选项的方法也不止一种。第一种方式是使用DOM的removeChild()方法并传入要移除的选项。第二种方式是使用选择框的remove()方法。这个方法接收一个参数,即要移除选项的索引。最后一种方式是直接将选项设置为等于null。这同样也是DOM之前浏览器实现的方式。 // 移除第一项 的三种实现方式 selectbox.removeChild(selecbox.options[0]); selecbox.remove(0); selecbox.options[0] = null; 要清除选择框的所有选项,需要迭代所有选项并逐一移除它们: function clearSelectbox(selectbox) { for (let option of selectbox.options) { selectbox.remove(0); } } 这个函数可以逐一移除选择框中的每一项。因为移除第一项会自动将所有选项向前移一位,所以这样就可以移除所有选项。 19.3.4 移动和重排选项在DOM之前,从一个选择框向另一个选择框移动选项非常麻烦,要先从第一个选择框移除选项,然后以相同文本和值创建新选项,再将新选项添加到第二个选择框。DOM方法则可直接将某个选项,从第一个选择框移动到第二个选择框,只要对相应选项使用appendChild()方法即可。如果给这个方法传入文档中已有的元素,则该元素会先从其父元素中移除,然后再插入指定位置。 let selectbox1 = document.getElementById('selLocations1'); let selectbox2 = document.getElementById('selLocations2'); selectbox2.appendChild(selectbox1.options[0]); 移动选项和移除选项都会导致每个选项的index属性重置。重排选项非常类似,DOM方法同样是最佳途径。要将选项移动到选择框中的特定位置,insertBefore()方法最合适。不过,要把选项移动到最后,还是appendChild()方法比较方便。 let optionToMove = selectbox.options[1]; selectbox.insertBefore(optionToMove, selectbox.options[optionToMove.index - 1]); 演示了将一个选项在选择框中前移一个位置。首先获得要移动选项的索引,然后将其插入之前位于它前面的选项之前,其中第二行代码适用于除第一个选项之外的所有选项。 let optionToMove = selectbox.options[1]; selectbox.insertBefore(optionToMove, selectbox.options[optionToMove.index + 2]); 可将选项向下移动一个位置。 19.4 表单序列化表单在JavaScript中可以使用表单字段的type属性连同其name属性和value属性来进行序列化。在写代码之前,我们需要理解浏览器如何确定在提交表单时要把什么发送到服务器:❑ 字段名和值是URL编码的并以和号(&)分隔。❑ 禁用字段不会发送。❑ 复选框或单选按钮只在被选中时才发送。❑ 类型为”reset”或”button”的按钮不会发送。❑ 多选字段的每个选中项都有一个值。❑ 通过点击提交按钮提交表单时,会发送该提交按钮;否则,不会发送提交按钮。类型为”image”的元素视同提交按钮。❑ 元素的值是被选中元素的value属性。如果元素没有value属性,则该值是它的文本。表单序列化通常不包含任何按钮,因为序列化得到的字符串很可能以其他方式提交。 19.5 富文本编辑基本的技术是在空白HTML文件中嵌入一个iframe。通过designMode属性,可将这个空白文档变成可以编辑的,实际编辑的则是元素的HTML。designMode属性有两个可能的值:”off”(默认值)和”on”。设置为”on”时,整个文档都会变成可以编辑的(显示插入光标),从而可以像使用文字处理程序一样编辑文本,通过键盘将文本标记为粗体、斜体,等等。作为iframe源的是一个非常简单的空白HTML页面。 <! DOCTYPE html> <html> <head> <title>作为富文本的空白页</title> </head> <body> </body> </html> 这个页面会像其他任何页面一样加载到iframe里。为了可以编辑,必须将文档的designMode属性设置为”on”。不过,只有在文档完全加载之后才可以设置。在这个包含页面内,需要使用onload事件处理程序在适当时机设置designMode <iframe name="richedit" style="height: 100px; width: 100px"></iframe> <script> window.addEventListener('load', () => { frames['richedit'].document.designMode = 'on'; }); </script> 19.5.1 使用contenteditable还有一种处理富文本的方式,也是IE最早实现的,即指定contenteditable属性。可以给页面中的任何元素指定contenteditable属性,然后该元素会立即被用户编辑。这种方式更受欢迎,因为不需要额外的iframe、空页面和JavaScript,只给元素添加一个contenteditable属性即可。 <div class="editable" id="richedit" contenteditable></div> 元素中包含的任何文本都会自动被编辑,元素本身类似于元素。<br />通过设置contentEditable属性,也可以随时切换元素的可编辑状态:</p> <pre><code class="lang-javascript">let div = document.getElementById('richedit'); richedit.contentEditable = 'true'; </code></pre> <p>contentEditable属性有3个可能的值:”true”表示开启,”false”表示关闭,”inherit”表示继承父元素的设置(因为在contenteditable元素内部会创建和删除元素) <a name="sLv8q"></a></p> <h2 id="5zs7zw"><a name="5zs7zw" class="reference-link"></a><span class="header-link octicon octicon-link"></span>19.5.2 与富文本交互</h2><p>document.execCommand()<br />这个方法在文档上执行既定的命令,可以实现大多数格式化任务。<br />接收3个参数:要执行的命令、表示浏览器是否为命令提供用户界面的布尔值,和执行命令必需的值(如果不需要则为null)。<br />为跨浏览器兼容,第二个参数应该始终为false,因为Firefox会在其为true时抛出错误。<br />与命令相关的其他一些方法:<br />第一个方法是queryCommandEnabled(),用于确定对当前选中文本或光标所在位置是否可以执行相关命令。<br />接收一个参数,即要检查的命令名。如果可编辑区可以执行该命令就返回true,否则返回false<br />另一个方法queryCommandState()用于确定相关命令是否应用到了当前文本选区。<br />最后一个方法是queryCommandValue(),此方法可以返回执行命令时使用的值(即前面示例的execCommand()中的第三个参数)。 <a name="uBtn5"></a></p> <h2 id="fn43k"><a name="fn43k" class="reference-link"></a><span class="header-link octicon octicon-link"></span>19.5.3 富文件选择</h2><p>在内嵌窗格中使用getSelection()方法,可以获得富文本编辑器的选区。这个方法暴露在document和window对象上,返回表示当前选中文本的Selection对象。<br />Selection对象的这个方法极其强大,充分利用了DOM范围来管理选区。操纵DOM范围可以实现比execCommand()更细粒度的控制,因为可以直接对选中文本的DOM内容进行操作。 <a name="b4jZr"></a></p> <h2 id="8zyky"><a name="8zyky" class="reference-link"></a><span class="header-link octicon octicon-link"></span>19.5.4 通过表单提交富文本</h2><p>因为富文本编辑是在内嵌窗格中或通过为元素指定contenteditable属性实现的,而不是在表单控件中实现,所以富文本编辑器技术上与表单没有关系。<br />这意味着要把富文本编辑的结果提交给服务器,必须手工提取HTML并自己提交。<br />通常的解决方案是在表单中添加一个隐藏字段,使用内嵌窗格或contenteditable元素的HTML更新它的值。在表单提交之前,从内嵌窗格或contenteditable元素中提取出HTML并插入隐藏字段中。<br />例如,以下代码在使用内嵌窗格实现富文本编辑时,可以用在表单的onsubmit事件处理程序中:</p> <pre><code class="lang-javascript">form.addEventListener('submit', (event) => { let target = event.target; target.elements['comments'].value = frames['richedit'].document.body.innerHTML; }) </code></pre> <p>代码使用文档主体的innerHTML属性取得了内嵌窗格的HTML,然后将其插入名为”comments”的表单字段中。这样做可以确保在提交表单之前给表单字段赋值。<br />如果使用submit()方法手工提交表单,那么要注意在提交前先执行上述操作。对于contenteditable元素,执行这一操作的代码是类似的:</p> <pre><code class="lang-javascript">form.addEventListener('submit', (event) => { let target = event.target; target.elements['comments'].value = document.getElementById('richedit').innerHTML; }) </code></pre>