DOM编程:http://www.apple.com.cn/developer/internet/webcontent/dom2i.html

DOM-2在浏览器中生成动态内容的应用
本文探讨了DOM-2中用于在浏览器中生成动态内容的基本功能。介绍了结点和元素的概念,以及创建元素和文本的方法。还讨论了innerHTML的使用场景,以及更多结点操作,如移除、代替和插入结点等。最后提及后续文章将介绍操作元素属性和创建动态效果等内容。

http://www.apple.com.cn/developer/internet/webcontent/dom2i.html

Dynamic Content with DOM-2 (Part I of II)

最近出现的对等网络分布式计算,以及人们对实时数据交换重新燃起的兴趣,已经使在 web 上显示动态内容这个热门话题烟消云散了。遗憾的是,HTTP 的无态性和不同浏览器中渲染构件的局限性,给希望把新鲜的信息传递给客户端而又不想向服务器端发送额外请求的 web 开发者们带来了重大的挑战。

为了应对这个挑战,开发者回退到 JavaScript 技巧的使用上来,比如巧妙地使用 document.write() 语句,frameset,以及 IFRAME,或者使用 Internet Explorer 的 innerHTML()insertAdjacentHTML() 函数来插入新的HTML内容,这种状态一直延续到最近。这些方法虽然在某种程度上是成功的,但是通常都和特定的浏览器实现互相耦合。这些技术的一个具体的缺陷就是很少把文档的结构作为一个问题来考虑。在绝大多数情况下,只是把一个又一个的 HTML 字符串简单地插入到页面上,很少考虑实际上正在渲染的是什么内容。

幸运的是,随着遵循 W3 的浏览器的出现,我们现在可以有更好的方法。被 IE5 和 NS6 两种浏览器所支持的二级文档对象模型(Document Object Model Level 2,即 DOM-2)提供了一种接口,使得开发人员可以在页面装载完成之后即时生成新的 HTML。通过调用 DOM-2 方法,可以创建 HTML 元素,定义元素的属性,并把元素附加到文档或者现有的元素中,从而实现HTML的即时生成。本文将探讨 DOM-2 中用于在浏览器中生成动态内容的一些基本功能。

本文假定您对正确的文档结构有一个一般的认识,并且把文档的概念理解为一系列聚合在一起的具有父子关系的HTML对象。

结点和元素

DOM-2 规范中把构成一个文档的所有项目都称为结点。结点接口提供了一系列公共的方法和属性,使得开发者可以访问和操作文档中的所有项目。

和 NS6(Mozilla)一样,IE5 把 HTML 文档中的所有项目或者识别为元素,或者识别为文本结点。理解元素和文本结点的基本差别是非常重要的。元素普遍地和由尖括号包围起来的标识相关联。在 HTML,所有的标识都是元素,比如 <P>,<IMG> 和 <DIV>。元素也可能有属性,或者包含子结点。

另一方面,文本结点代表一块文本。它和元素不同,没有属性或者子结点(虽然它从结点接口中继承了 childNodes 和属性集合)。

考虑下面的例子代码:

<span id="weather">Partly cloudy, scattered showers, high 67</span>

上面的代码由两个独立的结点组成。<SPAN >标识是一个元素结点,带有一个ID属性。在 SPAN 中间的文本实际上是一个独立的文本结点。这个结点只有文本,没有属性或者子结点。

元素结点和文本结点存在一些共同的属性:

  • nodeType:这个属性保存一个数字值,对应于结点的类型。元素的 nodeType 属性值为 1,而文本结点的 nodeType 属性值为 3。在操作中如果需要考察几个未知类型结点,则可以用这个属性来区分特定的结点类型。
  • nodeName:这个属性保存一个字符串。和 nodeType 一样,这个字符串和结点的类型相对应。所有的文本结点的 nodeName 属性值都为“#text”。对于元素,其 nodeName 属性值包含元素标识的名字。因此一个 HTML image 标识的 nodeName 属性值为“IMG”。
  • nodeValue:这个属性保存结点的值,如果有的话。元素的 nodeValue 属性值为 null。文本结点的 nodeValue值 是该结点包含的实际文本字符串。

如果您需要结点属性和方法的完整列表,可以参考 W3C 的 DOM-2 规范

创建元素和文本

文档对象中包含的一系列方法,使新结点的创建成为可能。这些方法是:

  • createElement(String type):创建指定类型的新元素,并返回该元素的一个引用。
  • createTextNode(String text):以text参数指定的内容创建一个新的文本结点,并返回该结点的一个引用。

createElement

创建一个新的元素结点出乎意料的简单。只要调用 createElement 方法,并传入您希望的元素类型就可以了。如果您希望对这个新建的元素执行进一步的操作,则把该方法的返回值--即这个新建元素的引用--赋值给一个变量将是一个很好的主意。

下面的代码创建了一个新的没有内容的段落元素:

 var newPara = document.createElement("p"); 

现在 newPara 变量就是该新建段落元素的一个引用。

createTextNode

您可以用类似的方式创建文本结点,即调用 createTextNode 方法,传入希望放入这个新建结点的文本字符串。和创建元素时一样,您可以把返回值保存到一个变量中,以便在随后的代码中引用。

 var newText = document.createTextNode("This is a new sentence."); 

前面的代码创建了两个新的结点,但是这两个结点在浏览器中不能看到,因为它们还没有被插入到文档中。新建的结点将自由地浮在 JavaScript 中,直到您把它们分配给一个父亲对象,或者文档体(body)对象的本身。

从结点接口继承下来的每一个对象都有一个名为 appendChild 的方法,这个方法使其它结点可以被插入到本结点。举例来说,通过使用 appendChild 方法,您可以把 newText 这个新的文本结点插入到名为 newPara 的段落元素中:

var newPara = document.createElement("p");
var newText = document.createTextNode("This is a new sentence.");
newPara.appendChild(newText);

现在,您需要做的,就是把段落元素插入到文档体对象中。为了实现这个目的,您需要得到文档的BODY元素的一个引用。有几种方法可以获得这个引用。多数比较新的浏览器版本(IE5 和 NS6)都允许您使用 document.body 语句,比如 document.body.appendChild(newPara);,然而这是一个非标准的便利属性,在未来版本中可能不能工作。

一个更加遵循标准的方法是通过文档对象的 getElementsByTagName 方法来获得文档中可以找到的第一个 BODY 元素的引用。

var bodyRef = document.getElementsByTagName("body").item(0);
bodyRef.appendChild(newPara);

方法 item(0) 是必要的,因为 getElementsByTagName 函数返回的是其根据指定标识名称找到的元素的集合。由于一个 HTML 页面中通常只有一个 BODY 标识,所以您可以直接使用 item 方法从集合中取得第一项。

还有一个遵循标准的方法可以用来获取文档体的引用,就是使用 getElementById 方法。然而,这个方法假定您已经不怕麻烦,为您的 HTML 页面的 <BODY> 标识分配了一个ID属性。

<body id="docBody">
var bodyRef = document.getElementById("docBody");
bodyRef.appendChild(newPara);

您也可以使用 getElementsByTagName getElementById 这两个方法来得到除了文档体对象之外的其它页面对象的引用。例如,点击这个连接就会激活一个脚本,该脚本使用 getElementById 方法来获得本文当前这个段落元素的引用(这个段落元素的 ID 属性被指定为“example1”),然后通过 appendChild 方法向这个段落元素中插入一个新的文本结点。

完成上述任务的代码如下所示:

<script type="text/javascript">
function insertNewText()
{
var newText = document.createTextNode
	(" DOM方法很酷,不是吗?");
var para = document.getElementById("example1");
para.appendChild(newText);
}
</script>

由于 ID 属性值为“example1”的段落元素已经存在于文档的 BODY 元素中,所以完全没有必要去引用文档体对象了。这是一个简单的例子,但是这个例子表现出来的概念可以用来进行其它复杂的DOM操作。

在创建新结点时有一些限制:一个是新创建的结点只能被插入到创建这个结点的文档中。这意味着如果您使用一系列的帧,则您不能在一个帧中创建一个元素,然后添加到另一个帧的文档对象中。还有,把一个元素从一个窗口传递到另外一个窗口也是不可能的。

还有一点值得一提,就是您也受限于文档的逻辑层次以及当前正在操作的结点类型。如果试着把一个节点插入到该结点本身或该结点自己的子结点,或者插入到不允许把该类型的结点作为子结点的其它结点对象中,则可能会出现错误。

在什么地方使用 innerHTML?

您可能会奇怪,为什么我们要用这些看起来很复杂的,很冗长的 JavaScript 语句来创建那么一点文本或者一个 HTML 元素?您完全可以使用 innerHTML 来完成这些工作呀。

innerHTML 是 Microsoft 公司作为读写一个元素的 HTML 内容而引入的便利属性。举例来说,通过 innerHTML 语句,您可以轻松创建一个具有多个格子的表结构,并将它插入到您的页面中:

var str = '<table border="1">';
str += '<tr><td>Cell One</td><td>Cell Two?	</td><td>Cell Three</td></tr>';
str += '</table>';
document.all["paragraphOne"].innerHTML = str;

innerHTML 已经被集成到 Mozilla 的代码库中,而且现在也是 NS6 的一部分了。因此我们为什么不用它呢?一个基本的原因就是 innerHTML 并不返回其创建的结点的任何引用。如果由于某些原因,需要修改这个新表的第二个TD元素中的文本,那您就没有办法找到第二个格子的引用了--而如果使用 createElement() 函数的话,则我们当时就已经拥有这个引用了,因为该函数总是返回结点的引用。您可以用 document.all,或者甚至是使用 document.getElementById() 函数来得到相应结点的引用,但是这些方法假定您已经为字符串中的每一个元素都正确地分配了唯一的 ID。如果是这样的话,重新书写整个字符串,并再次插入到 paragraphOne 对象中可能更容易一些。

您选择什么方法最终取决于您希望完成什么任务。如果您只是简单地显示文本或者 HTML,不需要访问新创建的元素,那就使用 innerHTML。然而,如果您计划在一个大一些的动态生成结构中对特定的对象进行操作,则需要花一些时间,通过 DOM 文档方法来进行对象的构造。

更多的结点操作

同添加新结点一样,您也可以移除现有的结点。removeChild 方法使得任何结点可以移除它自己的子结点,只要简单地传入您希望移除的结点的引用就可以了。在被移除的结点中的任何元素或者文本也将一起被移除。

当B元素被从段落中移除时,在这些黑体标识之间的文本,也会一起消失。

<script type="text/javascript">
function removeBElm(){
var para = document.getElementById("example2");
var boldElm = document.getElementById("example2B");
var removed = para.removeChild(boldElm);
} </script>

点击这个连接看看上面的脚本是如何动作的。

被移除的结点并没有被破坏。removeChild 函数会返回被移除的结点的引用,因此随后可以对这个结点进行其它的操作。请注意,在这个例子中我们把被移除的B元素赋值给 removed 变量,从而维护了该结点的一个引用。

如果您想要移除一个不存在的结点,或者一个被标注为只读的结点,则可能回出现错误,这取决于浏览器的实现。

代替结点

除了移除结点之外,DOM还提供了一个把一个结点代替为另一个结点的方法。replaceChild 方法就可以实现这个功能。和上面的 removeChild 方法一样,replaceChild 函数必须由包含希望被代替的结点的元素进行调用。

replaceChild 接收两个参数:一个是新结点的引用,另一个是即将被代替的结点的引用。下面的例子创建一个包含一个文本节点的新 SPAN 元素,并用它来代替现有的SPAN元素。

<script type="text/javascript">
function replaceSpan(){

var newSpan = document.createElement("span");
var newText = 
	document.createTextNode("受惊的斑马");
newSpan.appendChild(newText);

var para = document.getElementById("example3");
var spanElm = document.getElementById("ex3Span");
var replaced = para.replaceChild(newSpan,spanElm);
}
</script>

您可以在这个试一下这个例子:

敏捷的棕色狐狸在懒惰的狗上跳跃。

在一个结点系列中插入

您可能已经注意到,在先前的例子中,appendChild 函数总是把新添加的结点作为父结点的最后一个子结点。在某些时候,可能需要在现有结点之前或者之间插入结点。

insertBefore 这个结点方法可以实现这个目的。insertBefore 接收两个参数:即将被插入的新结点,以及您希望在哪个结点之前插入。下面的例子用 insertBefore 方法在 HTML 表格中的一个现有的 TD 前插入一个新的TD元素。

<table border="1">
	<tr id="example4">
		<td>TD One</td>
		<td>TD Two</td>
		<td>TD Three</td>
	</tr>
</table>

<script type="text/javascript">

var TDCount = 0;

function insertTD(){

	var newTD = document.createElement("td");
	var newText = document.createTextNode("New Cell " + (TDCount++));
	newTD.appendChild(newText);

	var trElm = document.getElementById("example4");
	var refTD = trElm.getElementsByTagName("td").item(2);
	trElm.insertBefore(newTD,refTD);
	
}
</script>

您可以在这里试一个这个例子:

TD OneTD TwoTD Three

前面的路

我们已经讨论了 DOM-2 如何使 HTML 的即时生成和页面操作变成如此的简单,然而您可能还需要更强大的功能。本文没有讨论如何影响元素的属性,比如表格的格子宽度,背景颜色等等。在下一篇文章中,您将会看到如何用DOM-2来操作元素的属性,以及创建动态效果,您还将看到其它一些结点的方法和属性,以及为了更方便地引用 DOM 树中对象而提供的元素接口。

更多信息

<think> 我们正在解决一个常见的JSP开发问题:无法解析标签库URI为http://java.sun.com/jsp/jstl/core。根据提供的引用信息,我们可以总结出以下几种解决方案: 1. 确保已正确导入JSTL库(通常需要将jstl.jar和standard.jar放在WEB-INF/lib目录下)[^1][^4]。 2. 检查web.xml中是否正确配置了taglib,尤其是在使用较高版本的Tomcat时(如Tomcat 8.5)[^3]。 3. 检查JSP页面中taglib指令的URI是否正确,有时版本问题可能导致URI变化。 根据引用[3],在web.xml中配置taglib是一种解决方案,但注意配置的URI必须与JSP页面中使用的URI一致。 具体步骤: 步骤1:确认JSTL库存在 确保项目中WEB-INF/lib目录下有jstl相关的jar包(如jstl-1.2.jar)。如果没有,需要下载并放入。 步骤2:检查JSP页面中的taglib指令 在JSP页面顶部,确保taglib指令如下: ```jsp <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> ``` 步骤3:在web.xml中配置taglib(可选,但有时必要) 在web.xml的<jsp-config>标签内添加如下配置(根据引用[3]): ```xml <jsp-config> <taglib> <taglib-uri>http://java.sun.com/jsp/jstl/core</taglib-uri> <taglib-location>/WEB-INF/lib/c.tld</taglib-location> </taglib> </jsp-config> ``` 注意:这个配置依赖于你是否有c.tld文件。在JSTL1.2中,通常不再需要这样配置,因为jar包中META-INF下的标签库描述符会自动被识别。所以这个步骤主要是针对旧版本或特殊情况。 步骤4:清理和重新部署 清理项目并重新部署到Tomcat,有时候旧的部署可能导致问题。 然而,根据引用[1]和[4],更常见的解决方案是直接添加JSTL的jar包。在Tomcat 8.5中,推荐使用JSTL 1.2版本。 因此,我们建议: 1. 下载JSTL 1.2的jar包(可以从Maven仓库等获取),包括: - jstl-1.2.jar 2. 将jar包放入项目的WEB-INF/lib目录下。 3. 如果问题仍然存在,尝试在web.xml中添加上述的taglib配置(注意:确保<taglib-location>指向正确的tld文件位置,但在JSTL1.2中,tld文件在jar包内,所以通常不需要配置web.xml)。 如果使用Maven,可以在pom.xml中添加依赖: ```xml <dependency> <groupId>javax.servlet</groupId> <artifactId>jstl</artifactId> <version>1.2</version> </dependency> ``` 总结:最可能的原因是缺少JSTL库,所以请先确保库已正确引入。 如果以上步骤不能解决,请检查Tomcat的lib目录下是否有冲突的jar包(例如tomcat自带的jstl库)?如果有,可能需要移除。 根据引用[4],还可能是由于Tomcat版本较高,而项目没有正确加载JSTL。确保项目构建路径中包含了JSTL库。 注意:在Tomcat 10及以上版本,由于包名从javax变为jakarta,需要使用支持Jakarta EE的JSTL(例如使用jakarta.servlet.jsp.jstl-api和jakarta.servlet.jsp.jstl)。 但问题中用户使用的是Tomcat 8.5,所以还是javax包。因此使用JSTL 1.2即可。 因此,我们给出以下解决方案: 解决方案1(推荐): 添加JSTL 1.2的jar包到WEB-INF/lib目录下。 解决方案2(如果方案1无效): 在web.xml中配置taglib,但需要确保你有正确的tld文件(通常我们不需要,因为jar中自带)。配置示例(注意uri必须和jsp中的一致): ```xml <web-app ...> ... <jsp-config> <taglib> <taglib-uri>http://java.sun.com/jsp/jstl/core</taglib-uri> <taglib-location>/WEB-INF/lib/jstl-1.2.jar</taglib-location> <!-- 实际上,这样配置并不正确,因为taglib-location应该是一个tld文件路径,而不是jar。所以更常见的做法是解压jar,找到c.tld,然后指定其路径,例如:/WEB-INF/tld/c.tld。但是,我们不推荐这样做,因为JSTL1.2的设计是自动识别的。所以,如果方案1不行,考虑其他原因。 --> </taglib> </jsp-config> </web-app> ``` 实际上,在JSTL1.2中,tld文件位于META-INF目录下,容器会自动识别,所以通常不需要在web.xml中配置。 因此,我们回到根本:确保jar包存在。 另外,注意项目部署时,WEB-INF/lib下的jar是否被正确加载。可以检查Tomcat的logs目录下的日志,看是否有加载jstl库的信息,或者是否有相关错误。 最后,如果使用的是IDE(如Eclipse),请确保jar包被添加到部署程序集中(在Eclipse中,项目属性->Deployment Assembly里添加)。 所以,我们给出以下步骤: 1. 下载jstl-1.2.jar。 2. 复制到项目的WebContent/WEB-INF/lib(或webapp/WEB-INF/lib)目录下。 3. 重新部署项目。 如果问题仍然存在,请检查项目构建路径:确保该jar在构建路径中(但在IDE中,放在WEB-INF/lib下通常会自动加入构建路径)。 另一种可能:如果项目是通过Maven管理的,确保依赖作用域(scope)为默认(compile),并且已经正确下载。 综上所述,最直接有效的解决方案是确保WEB-INF/lib下有jstl-1.2.jar。 根据引用,我们总结了解法,并提供了详细步骤。 </think>### 无法解析标签库URI:http://java.sun.com/jsp/jstl/core 解决方案 此问题通常发生在Tomcat部署JSP项目时,主要原因是**缺少JSTL库文件**或**配置错误**。以下是已验证的解决方案: #### ✅ 解决方案1:添加JSTL依赖库(推荐) 1. **下载JSTL 1.2版本** 访问 [Maven仓库](https://mvnrepository.com/artifact/javax.servlet/jstl/1.2) 下载 `jstl-1.2.jar` ```bash wget https://repo1.maven.org/maven2/javax/servlet/jstl/1.2/jstl-1.2.jar ``` 2. **将JAR包放入项目** 复制 `jstl-1.2.jar` 到项目的 `WEB-INF/lib` 目录 ```markdown your-project/ ├── WEB-INF/ │ ├── lib/ │ │ └── jstl-1.2.jar ✅ 关键文件 │ └── web.xml ``` 3. **清理并重启Tomcat** ```bash rm -rf $CATALINA_HOME/work/Catalina/localhost/your-app/* $CATALINA_HOME/bin/shutdown.sh $CATALINA_HOME/bin/startup.sh ``` > **原理**:Tomcat 8.5+ 不再内置JSTL,需手动添加[^1][^4] #### ⚙️ 解决方案2:检查web.xml配置(备用) 若已添加JAR包仍报错,在 `web.xml` 中添加: ```xml <jsp-config> <taglib> <taglib-uri>http://java.sun.com/jsp/jstl/core</taglib-uri> <taglib-location>/WEB-INF/lib/jstl-1.2.jar</taglib-location> </taglib> </jsp-config> ``` > 注意:此配置通常不需要,仅当容器无法自动加载时使用[^3] #### 🔍 验证步骤 在JSP页面添加测试代码: ```jsp <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> <c:set var="test" value="Hello JSTL"/> ${test} <%-- 应显示"Hello JSTL"--%> ``` #### ⚠️ 常见错误排查 1. **冲突的旧版本** 删除 `WEB-INF/lib` 下的 `jstl.jar` 和 `standard.jar`(1.1版本) 2. **部署问题** 检查Tomcat控制台日志: ```log INFO [main] org.apache.catalina.startup.HostConfig.deployDirectory Deploying web application directory [/path/to/your-app] ``` 3. **IDE缓存问题** 在Eclipse/IDEA中执行: - Project → Clean - Maven → Update Project --- ### 📚 技术原理 当JSP引擎解析 `<%@ taglib uri="http://java.sun.com/jsp/jstl/core" %>` 时: 1. 在 `web.xml` 中查找匹配的 `<taglib-uri>` 2. 未找到时搜索 `WEB-INF/lib/*.jar` 中的 `META-INF/*.tld` 文件 3. JSTL 1.2 的TLD文件位于 `jstl-1.2.jar!/META-INF/c.tld` 4. 缺少JAR包会导致URI解析失败[^2][^3] ---
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值