吴裕雄--天生自然轻量级JAVA EE企业应用开发Struts2Sping4Hibernate整合开发学习笔记:JSP2的自定义标签

<%--
网站: <a href="http://www.crazyit.org">疯狂Java联盟</a>
author  yeeku.H.lee kongyeeku@163.com
version  1.0
Copyright (C), 2001-2016, yeeku.H.Lee
This program is protected by copyright laws.
Program Name:
Date: 
--%>

<%@ page contentType="text/html; charset=GBK" language="java" errorPage="" %>
<!-- 导入标签库,指定mytag前缀的标签,
    由http://www.crazyit.org/mytaglib的标签库处理 -->
<%@ taglib uri="http://www.crazyit.org/mytaglib" prefix="mytag"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <title>自定义标签示范</title>
    <meta name="website" content="http://www.crazyit.org" />
</head>
<body bgcolor="#ffffc0">
<h2>下面显示的是自定义标签中的内容</h2>
<h4>指定两个属性</h4>
<mytag:dynaAttr name="crazyit" url="crazyit.org"/><br/>
<h4>指定四个属性</h4>
<mytag:dynaAttr 书名="疯狂Java讲义" 价格="99.0"
    出版时间="2008年" 描述="Java图书"/><br/>
</body>
</html>
<%--
网站: <a href="http://www.crazyit.org">疯狂Java联盟</a>
author  yeeku.H.lee kongyeeku@163.com
version  1.0
Copyright (C), 2001-2016, yeeku.H.Lee
This program is protected by copyright laws.
Program Name:
Date: 
--%>

<%@ page contentType="text/html; charset=GBK" language="java" errorPage="" %>
<!-- 导入标签库,指定mytag前缀的标签,
    由http://www.crazyit.org/mytaglib的标签库处理 -->
<%@ taglib uri="http://www.crazyit.org/mytaglib" prefix="mytag"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <title>自定义标签示范</title>
    <meta name="website" content="http://www.crazyit.org" />
</head>
<body bgcolor="#ffffc0">
<h2>下面显示的是自定义标签中的内容</h2>
<mytag:fragment>
    <jsp:attribute name="fragment">
    <%-- 使用jsp:attribute标签传入fragment参数(该注释不能放在fragment内) -->
        <%-- 下面是动态的JSP页面片段 --%>
        <mytag:helloWorld/>
    </jsp:attribute>
</mytag:fragment>
<br/>
<mytag:fragment>
    <jsp:attribute name="fragment">
        <%-- 下面是动态的JSP页面片段 --%>
        ${pageContext.request.remoteAddr}
    </jsp:attribute>
</mytag:fragment>
</body>
</html>
<%--
网站: <a href="http://www.crazyit.org">疯狂Java联盟</a>
author  yeeku.H.lee kongyeeku@163.com
version  1.0
Copyright (C), 2001-2016, yeeku.H.Lee
This program is protected by copyright laws.
Program Name:
Date: 
--%>

<%@ page contentType="text/html; charset=GBK" language="java" errorPage="" %>
<!-- 导入标签库,指定mytag前缀的标签,
    由URI为http://www.crazyit.org/mytaglib的标签库处理 -->
<%@ taglib uri="http://www.crazyit.org/mytaglib" prefix="mytag"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <title>自定义标签示范</title>
    <meta name="website" content="http://www.crazyit.org" />
</head>
<body bgcolor="#ffffc0">
<h2>下面显示的是自定义标签中的内容</h2>
<!-- 使用标签 ,其中mytag是标签前缀,根据taglib的编译指令,
    mytag前缀将由URI为http://www.crazyit.org/mytaglib的标签库处理 -->
<mytag:helloWorld/><br/>
</body>
</html>
<%--
网站: <a href="http://www.crazyit.org">疯狂Java联盟</a>
author  yeeku.H.lee kongyeeku@163.com
version  1.0
Copyright (C), 2001-2016, yeeku.H.Lee
This program is protected by copyright laws.
Program Name:
Date: 
--%>

<%@ page contentType="text/html; charset=GBK" language="java" errorPage="" %>
<%@ page import="java.util.*"%>
<!-- 导入标签库,指定mytag前缀的标签,
由http://www.crazyit.org/mytaglib的标签库处理 -->
<%@ taglib uri="http://www.crazyit.org/mytaglib" prefix="mytag"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <title> 带标签体的标签-迭代器标签 </title>
    <meta name="website" content="http://www.crazyit.org" />
</head>
<body>
    <h2>带标签体的标签-迭代器标签</h2><hr/>
    <%
    //创建一个List对象
    List<String> a = new ArrayList<String>();
    a.add("疯狂Java");
    a.add("www.crazyit.org");
    a.add("www.fkit.org");
    //将List对象放入page范围内
    pageContext.setAttribute("a" , a);
    %>
    <table border="1" bgcolor="#aaaadd" width="300">
        <!-- 使用迭代器标签,对a集合进行迭代 -->
        <mytag:iterator collection="a" item="item">
        <tr>
            <td>${pageScope.item}</td>
        <tr>
        </mytag:iterator>
    </table>
</body>
</html>
<%--
网站: <a href="http://www.crazyit.org">疯狂Java联盟</a>
author  yeeku.H.lee kongyeeku@163.com
version  1.0
Copyright (C), 2001-2016, yeeku.H.Lee
This program is protected by copyright laws.
Program Name:
Date: 
--%>

<%@ page contentType="text/html; charset=GBK" language="java" errorPage="" %>
<!-- 导入标签库,指定mytag前缀的标签,
    由http://www.crazyit.org/mytaglib的标签库处理 -->
<%@ taglib uri="http://www.crazyit.org/mytaglib" prefix="mytag"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <title>自定义标签示范</title>
    <meta name="website" content="http://www.crazyit.org" />
</head>
<body bgcolor="#ffffc0">
<h2>下面显示的是查询标签的结果</h2>
<!-- 使用标签 ,其中mytag是标签前缀,根据taglib的编译指令,
    mytag前缀将由http://www.crazyit.org/mytaglib的标签库处理 -->
<mytag:query
    driver="com.mysql.jdbc.Driver"
    url="jdbc:mysql://localhost:3306/javaee"
    user="root"
    pass="32147"
    sql="select * from news_inf"/><br/>
</body>
</html>
<?xml version="1.0" encoding="GBK"?>
<!-- 定义生成文件的project根元素,默认的target为空 -->
<project name="web" basedir="." default="">
    <!-- 定义三个简单属性 -->
    <property name="src" value="src"/>
    <property name="classes" value="classes"/>
    <!-- 定义一组文件和目录集 -->
    <path id="classpath">
        <fileset dir="lib">
            <include name="**/*.jar"/>
        </fileset>
        <pathelement path="${classes}"/>
    </path>
    <!-- 定义compile target,用于编译Java源文件 -->
    <target name="compile" description="编译Java源文件">
        <!-- 先删除classes属性所代表的文件夹 -->
        <delete dir="${classes}"/>
        <!-- 创建classes属性所代表的文件夹 -->
        <mkdir dir="${classes}"/>
        <copy todir="${classes}">
            <fileset dir="${src}">
                <exclude name="**/*.java"/>
            </fileset>
        </copy>
        <!-- 编译Java文件,编译后的class文件放到classes属性所代表的文件夹内 -->
        <javac destdir="${classes}" debug="true" includeantruntime="yes"
            deprecation="false" optimize="false" failonerror="true">
            <!-- 指定需要编译的Java文件所在的位置 -->
            <src path="${src}"/>
            <!-- 指定编译Java文件所需要第三方类库所在的位置 -->
            <classpath refid="classpath"/>
        </javac>
    </target>
</project>
<?xml version="1.0" encoding="GBK"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
    http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
    version="3.1">

</web-app>
<?xml version="1.0" encoding="GBK"?>

<taglib xmlns="http://java.sun.com/xml/ns/javaee"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/javaee 
    http://java.sun.com/xml/ns/javaee/web-jsptaglibrary_2_0.xsd"
    version="2.0">
    <tlib-version>1.0</tlib-version>
    <short-name>mytaglib</short-name>
    <!-- 定义该标签库的URI -->
    <uri>http://www.crazyit.org/mytaglib</uri>

    <!-- 定义第一个标签 -->
    <tag>
        <!-- 定义标签名 -->
        <name>helloWorld</name>
        <!-- 定义标签处理类 -->
        <tag-class>lee.HelloWorldTag</tag-class>
        <!-- 定义标签体为空 -->
        <body-content>empty</body-content>
    </tag>

    <!-- 定义第二个标签 -->
    <tag>
        <!-- 定义标签名 -->
        <name>query</name>
        <!-- 定义标签处理类 -->
        <tag-class>lee.QueryTag</tag-class>
        <!-- 定义标签体为空 -->
        <body-content>empty</body-content>
        <!-- 配置标签属性:driver -->
        <attribute>
            <name>driver</name> 
            <required>true</required>
            <fragment>true</fragment>
        </attribute>
        <!-- 配置标签属性:url -->
        <attribute>
            <name>url</name> 
            <required>true</required>
            <fragment>true</fragment>
        </attribute>
        <!-- 配置标签属性:user -->
        <attribute>
            <name>user</name> 
            <required>true</required>
            <fragment>true</fragment>
        </attribute>
        <!-- 配置标签属性:pass -->
        <attribute>
            <name>pass</name> 
            <required>true</required>
            <fragment>true</fragment>
        </attribute>
        <!-- 配置标签属性:sql -->
        <attribute>
            <name>sql</name> 
            <required>true</required>
            <fragment>true</fragment>
        </attribute>
    </tag>

    <!-- 定义第三个标签 -->
    <tag>
        <!-- 定义标签名 -->
        <name>iterator</name>
        <!-- 定义标签处理类 -->
        <tag-class>lee.IteratorTag</tag-class>
        <!-- 定义标签体不允许出现JSP脚本 -->
        <body-content>scriptless</body-content>
        <!-- 配置标签属性:collection -->
        <attribute>
            <name>collection</name> 
            <required>true</required>
            <fragment>true</fragment>
        </attribute>
        <!-- 配置标签属性:item -->
        <attribute>
            <name>item</name> 
            <required>true</required>
            <fragment>true</fragment>
        </attribute>
    </tag>
    <tag>
        <!-- 定义标签名 -->
        <name>fragment</name>
        <!-- 定义标签处理类 -->
        <tag-class>lee.FragmentTag</tag-class>
        <!-- 指定该标签不支持标签体 -->
        <body-content>empty</body-content>
        <!-- 定义标签属性:fragment -->
        <attribute>
            <name>fragment</name>
            <required>true</required>
            <fragment>true</fragment>
        </attribute>
    </tag>
    <!-- 定义接受动态属性的标签 -->
    <tag>
        <name>dynaAttr</name>
        <tag-class>lee.DynaAttributesTag</tag-class>
        <body-content>empty</body-content>
        <!-- 指定支持动态属性 -->
        <dynamic-attributes>true</dynamic-attributes>
    </tag>
</taglib>
<?xml version="1.0" encoding="GBK"?>

<taglib xmlns="http://xmlns.jcp.org/xml/ns/javaee"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee 
    http://xmlns.jcp.org/xml/ns/javaee/web-jsptaglibrary_2_1.xsd"
    version="2.1">
    <tlib-version>1.0</tlib-version>
    <short-name>mytaglib</short-name>
    <!-- 定义该标签库的URI -->
    <uri>http://www.crazyit.org/mytaglib</uri>

    <!-- 定义第一个标签 -->
    <tag>
        <!-- 定义标签名 -->
        <name>helloWorld</name>
        <!-- 定义标签处理类 -->
        <tag-class>lee.HelloWorldTag</tag-class>
        <!-- 定义标签体为空 -->
        <body-content>empty</body-content>
    </tag>

    <!-- 定义第二个标签 -->
    <tag>
        <!-- 定义标签名 -->
        <name>query</name>
        <!-- 定义标签处理类 -->
        <tag-class>lee.QueryTag</tag-class>
        <!-- 定义标签体为空 -->
        <body-content>empty</body-content>
        <!-- 配置标签属性:driver -->
        <attribute>
            <name>driver</name> 
            <required>true</required>
            <fragment>true</fragment>
        </attribute>
        <!-- 配置标签属性:url -->
        <attribute>
            <name>url</name> 
            <required>true</required>
            <fragment>true</fragment>
        </attribute>
        <!-- 配置标签属性:user -->
        <attribute>
            <name>user</name> 
            <required>true</required>
            <fragment>true</fragment>
        </attribute>
        <!-- 配置标签属性:pass -->
        <attribute>
            <name>pass</name> 
            <required>true</required>
            <fragment>true</fragment>
        </attribute>
        <!-- 配置标签属性:sql -->
        <attribute>
            <name>sql</name> 
            <required>true</required>
            <fragment>true</fragment>
        </attribute>
    </tag>

    <!-- 定义第三个标签 -->
    <tag>
        <!-- 定义标签名 -->
        <name>iterator</name>
        <!-- 定义标签处理类 -->
        <tag-class>lee.IteratorTag</tag-class>
        <!-- 定义标签体不允许出现JSP脚本 -->
        <body-content>scriptless</body-content>
        <!-- 配置标签属性:collection -->
        <attribute>
            <name>collection</name> 
            <required>true</required>
            <fragment>true</fragment>
        </attribute>
        <!-- 配置标签属性:item -->
        <attribute>
            <name>item</name> 
            <required>true</required>
            <fragment>true</fragment>
        </attribute>
    </tag>
    <tag>
        <!-- 定义标签名 -->
        <name>fragment</name>
        <!-- 定义标签处理类 -->
        <tag-class>lee.FragmentTag</tag-class>
        <!-- 指定该标签不支持标签体 -->
        <body-content>empty</body-content>
        <!-- 定义标签属性:fragment -->
        <attribute>
            <name>fragment</name>
            <required>true</required>
            <fragment>true</fragment>
        </attribute>
    </tag>
    <!-- 定义接受动态属性的标签 -->
    <tag>
        <name>dynaAttr</name>
        <tag-class>lee.DynaAttributesTag</tag-class>
        <body-content>empty</body-content>
        <!-- 指定支持动态属性 -->
        <dynamic-attributes>true</dynamic-attributes>
    </tag>
</taglib>
package lee;

import javax.servlet.jsp.tagext.*;
import javax.servlet.jsp.*;
import java.io.*;
import java.util.*;

/**
 * Description:
 * <br/>网站: <a href="http://www.crazyit.org">疯狂Java联盟</a>
 * <br/>Copyright (C), 2001-2016, Yeeku.H.Lee
 * <br/>This program is protected by copyright laws.
 * <br/>Program Name:
 * <br/>Date:
 * @author  Yeeku.H.Lee kongyeeku@163.com
 * @version  1.0
 */
public class DynaAttributesTag
    extends SimpleTagSupport implements DynamicAttributes
{
    // 保存每个属性名的集合
    private ArrayList<String> keys = new ArrayList<String>();
    // 保存每个属性值的集合
    private ArrayList<Object> values = new ArrayList<Object>();

    @Override
    public void doTag() throws JspException, IOException
    {
        JspWriter out = getJspContext().getOut();
        // 此处只是简单地输出每个属性
        out.println("<ol>");
        for( int i = 0; i < keys.size(); i++ )
        {
            String key = keys.get( i );
            Object value = values.get( i );
            out.println( "<li>" + key + " = " + value + "</li>" );
        }
        out.println("</ol>");
    }

    @Override
    public void setDynamicAttribute( String uri, String localName,
        Object value )throws JspException
    {
        // 添加属性名
        keys.add( localName );
        // 添加属性值
        values.add( value );
    }
}
package lee;

import javax.servlet.jsp.tagext.*;
import javax.servlet.jsp.*;
import java.io.*;

/**
 * Description:
 * <br/>网站: <a href="http://www.crazyit.org">疯狂Java联盟</a>
 * <br/>Copyright (C), 2001-2016, Yeeku.H.Lee
 * <br/>This program is protected by copyright laws.
 * <br/>Program Name:
 * <br/>Date:
 * @author  Yeeku.H.Lee kongyeeku@163.com
 * @version  1.0
 */

public class FragmentTag extends SimpleTagSupport
{
    private JspFragment fragment;

    // fragment的setter和getter方法
    public void setFragment(JspFragment fragment)
    {
        this.fragment = fragment;
    }
    public JspFragment getFragment()
    {
        return this.fragment;
    }
    @Override
    public void doTag() throws JspException, IOException
    {
        JspWriter out = getJspContext().getOut();
        out.println("<div style='padding:10px;border:1px solid black;"
            + ";border-radius:20px'>");
        out.println("<h3>下面是动态传入的JSP片段</h3>");
        // 调用、输出“页面片段”
        fragment.invoke( null );
        out.println("</div");
    }
}
package lee;

import javax.servlet.jsp.tagext.*;
import javax.servlet.jsp.*;
import java.io.*;

/**
 * Description:
 * <br/>网站: <a href="http://www.crazyit.org">疯狂Java联盟</a>
 * <br/>Copyright (C), 2001-2016, Yeeku.H.Lee
 * <br/>This program is protected by copyright laws.
 * <br/>Program Name:
 * <br/>Date:
 * @author  Yeeku.H.Lee kongyeeku@163.com
 * @version  1.0
 */
public class HelloWorldTag extends SimpleTagSupport
{
    // 重写doTag()方法,该方法为标签生成页面内容
    public void doTag()throws JspException,
        IOException
    {
        // 获取页面输出流,并输出字符串
        getJspContext().getOut().write("Hello World "
            + new java.util.Date());
    }
}
package lee;

import javax.servlet.jsp.tagext.*;
import javax.servlet.jsp.*;
import java.io.*;
import java.sql.*;
import java.util.*;

/**
 * Description:
 * <br/>网站: <a href="http://www.crazyit.org">疯狂Java联盟</a>
 * <br/>Copyright (C), 2001-2016, Yeeku.H.Lee
 * <br/>This program is protected by copyright laws.
 * <br/>Program Name:
 * <br/>Date:
 * @author  Yeeku.H.Lee kongyeeku@163.com
 * @version  1.0
 */
public class IteratorTag extends SimpleTagSupport
{
    // 标签属性,用于指定需要被迭代的集合
    private String collection;
    // 标签属性,指定迭代集合元素,为集合元素指定的名称
    private String item;

    // collection的setter和getter方法
    public void setCollection(String collection)
    {
        this.collection = collection;
    }
    public String getCollection()
    {
        return this.collection;
    }
    // item的setter和getter方法
    public void setItem(String item)
    {
        this.item = item;
    }
    public String getItem()
    {
        return this.item;
    }
    // 标签的处理方法,标签处理类只需要重写doTag()方法
    public void doTag() throws JspException, IOException
    {
        // 从page scope中获取名为collection的集合
        Collection itemList = (Collection)getJspContext().
            getAttribute(collection);
        // 遍历集合
        for (Object s : itemList)
        {
            // 将集合的元素设置到page范围内
            getJspContext().setAttribute(item, s );
            // 输出标签体
            getJspBody().invoke(null);
        }
    }
}
package lee;

import javax.servlet.jsp.tagext.*;
import javax.servlet.jsp.*;
import java.io.*;
import java.sql.*;

/**
 * Description:
 * <br/>网站: <a href="http://www.crazyit.org">疯狂Java联盟</a>
 * <br/>Copyright (C), 2001-2016, Yeeku.H.Lee
 * <br/>This program is protected by copyright laws.
 * <br/>Program Name:
 * <br/>Date:
 * @author  Yeeku.H.Lee kongyeeku@163.com
 * @version  1.0
 */
public class QueryTag extends SimpleTagSupport
{
    // 定义成员变量来代表标签的属性
    private String driver;
    private String url;
    private String user;
    private String pass;
    private String sql;

    // driver的setter和getter方法
    public void setDriver(String driver)
    {
        this.driver = driver;
    }
    public String getDriver()
    {
        return this.driver;
    }

    // url的setter和getter方法
    public void setUrl(String url)
    {
        this.url = url;
    }
    public String getUrl()
    {
        return this.url;
    }

    // user的setter和getter方法
    public void setUser(String user)
    {
        this.user = user;
    }
    public String getUser()
    {
        return this.user;
    }

    // pass的setter和getter方法
    public void setPass(String pass)
    {
        this.pass = pass;
    }
    public String getPass()
    {
        return this.pass;
    }

    // sql的setter和getter方法
    public void setSql(String sql)
    {
        this.sql = sql;
    }
    public String getSql()
    {
        return this.sql;
    }

    // conn的setter和getter方法
    public void setConn(Connection conn)
    {
        this.conn = conn;
    }
    public Connection getConn()
    {
        return this.conn;
    }

    // stmt的setter和getter方法
    public void setStmt(Statement stmt)
    {
        this.stmt = stmt;
    }
    public Statement getStmt()
    {
        return this.stmt;
    }

    // rs的setter和getter方法
    public void setRs(ResultSet rs)
    {
        this.rs = rs;
    }
    public ResultSet getRs()
    {
        return this.rs;
    }

    // rsmd的setter和getter方法
    public void setRsmd(ResultSetMetaData rsmd)
    {
        this.rsmd = rsmd;
    }
    public ResultSetMetaData getRsmd()
    {
        return this.rsmd;
    }
    // 执行数据库访问的对象
    private Connection conn = null;
    private Statement stmt = null;
    private ResultSet rs = null;
    private ResultSetMetaData rsmd = null;
    public void doTag()throws JspException,
        IOException
    {
           try
        {
            // 注册驱动
            Class.forName(driver);
            // 获取数据库连接
            conn = DriverManager.getConnection(url,user,pass);
            // 创建Statement对象
            stmt = conn.createStatement();
            // 执行查询
            rs = stmt.executeQuery(sql);
            rsmd = rs.getMetaData();
            // 获取列数目
            int columnCount = rsmd.getColumnCount();
            // 获取页面输出流
            Writer out = getJspContext().getOut();
            // 在页面输出表格
            out.write("<table border='1' bgColor='#9999cc' width='400'>");
            // 遍历结果集
            while (rs.next())
            {
                out.write("<tr>");
                // 逐列输出查询到的数据
                for (int i = 1 ; i <= columnCount ; i++ )
                {
                    out.write("<td>");
                    out.write(rs.getString(i));
                    out.write("</td>");
                }
                out.write("</tr>");
            }
        }
        catch(ClassNotFoundException cnfe)
        {
            cnfe.printStackTrace();
            throw new JspException("自定义标签错误" + cnfe.getMessage());
        }
        catch (SQLException ex)
        {
            ex.printStackTrace();
            throw new JspException("自定义标签错误" + ex.getMessage());
        }
        finally
        {
            // 关闭结果集
            try
            {
                if (rs != null)
                    rs.close();
                if (stmt != null)
                    stmt.close();
                if (conn != null)
                    conn.close();
            }
            catch (SQLException sqle)
            {
                sqle.printStackTrace();
            }
        }
    }
}
原文地址:https://www.cnblogs.com/tszr/p/12363994.html