5. 页面的样式
我们可以这样理解服务器端:后端程序实现了业务逻辑与内容的分离。网页开发人员可以专注于页面框架和功能的设计,而无需关心具体的内容,也不必为每一个内容分别开发一个页面。而内容可以由专人运营,不需要依靠开发人员,也就是说:发布新闻的活儿还是交给编辑来做,网店的商品交给小二来管理。
而另一方面,开发网页还需要实现内容与式样的分离。还是以发布新闻为例,网站编辑准备好新闻稿件,一般情况下,他不需要关注标题使用多大的字号,正文使用什么字体,什么颜色,每段文字之间空行多少,怎么对齐。可以理解,肯定存在一个样式的模板,每一篇新闻稿件发布之后,就会套用这个模板的样式。
这个定义样式的任务,最早是包含在html文档代码中的。在html规范中,定义了<b><i>
等标签,分别表示粗体、斜体。后来发现用标签表达式样不够用了,就发展出了专门用来描述样式的语言,称为CSS(层叠样式表,Cascading Style Sheets)。
有了CSS以后,就实现了功能、内容与式样的分离,就可以有专业的设计人员负责设计网页内容的呈现样式。CSS的代码既可以内嵌在HTML页面代码中,也可以作为一个单独的文档,由HTML页面调用。这里先看一个简单的例子
<html>
<head>
<style type="text/css">
body {color:red}
h1 {color:#00ff00}
p.bl {color:rgb(0,0,255)}
</style>
</head>
<body>
<h1>这是 heading 1</h1>
<p>这是一段普通的段落。默认是红色的。</p>
<p class="bl">这里的文本是蓝色的。</p>
</body>
</html>
以下是网页呈现的效果:
CSS的表现力是非常惊人的。
6. 用户的前端交互
到目前为止,我们还只接触到后端的技术,浏览器接收到的内容——网页、CSS样式表,都是服务器端生成好的静态内容,由浏览器负责呈现而已。那么不禁要问,后端既然可以通过程序语言动态地生成网页文件,前端(浏览器)是否可以通过程序语言动态地呈现网页文件呢?答案是肯定的。
来看这几种场景:
- 页面上的元素响应用户的动作,与用户有互动。例如导航菜单。鼠标放在一个栏目,会出现下拉菜单,鼠标移开,则菜单消失。又如淘宝首页的广告图片展示栏,会有多幅广告左右切换。
- 由浏览器执行的处理逻辑。例如用户输入的检验:用户注册页面,要求用户输入电子邮箱地址时,需要判断用户输入的格式。
由浏览器从服务器端加载的程序被成为Script(脚本),最流行的脚本语言就是Javascript。可以使用Javascript对事件作出响应、改变HTML内容,甚至是输出新的HTML代码。
Javascript程序也可以插入在HTML代码中,与后端程序PHP不同,服务器在处理HTML页面上,并不会处理Javascript代码,而是原封不动地送出来。浏览器在解析页面进行呈现时,才会依次运行Javascript代码。
案例1:消息窗
<html>
<head>
<script type="text/javascript">
function show_confirm()
{
var r=confirm("Press a button!");
if (r==true)
{
alert("You pressed OK!");
}
else
{
alert("You pressed Cancel!");
}
}
</script>
</head>
<body>
<input type="button" onclick="show_confirm()" value="Show a confirm box" />
</body>
</html>
首先看这个案例的HTML代码部分,在网页中有一个按钮,按钮绑定了一个Onclick
事件,即当用户点击按钮时,会触发Onclick
事件,并调用show_confirm()
函数。
再来看Javascript部分,直接定义了show_confirm()
函数。先弹出一个带确认按钮的消息框,再根据用户的选择OK或Cancel,弹出一个消息框。
此例中,弹出消息框、判断用户输入的逻辑,都是由浏览器执行的。
案例2:定时告警窗
<html>
<head>
<script type="text/javascript">
function timedMsg()
{
var t=setTimeout("alert('5 秒!')",5000)
}
</script>
</head>
<body>
<form>
<input type="button" value="显示定时的警告框" onClick = "timedMsg()">
</form>
<p>请点击上面的按钮。警告框会在 5 秒后显示。</p>
</body>
</html>
HTML部分还是一个简单的按钮,onClick
事件触发一个timeMsg()
函数。函数在Javascript代码中定义。setTimeout
函数是指在5000ms之后被自动调用,调用的执行内容是运行alert('5秒!')
这个语句。
这两个案例中,代码都是响应用户的动作后而执行的。服务器在生成HTML文件的时候,根本不知道用户在什么时候会按下按钮,所以他无法事先决定消息窗呈现的具体时间。因此他只能把预设的规则放在代码中,让浏览器自行处理。
7. 异步刷新页面
自从有了前端脚本语言,网页就从一个相对静态的媒体文件,逐渐转变成了功能越来越强大的应用程序界面。你可以理解为,当你访问一个网址时,浏览器是从服务器下载了一个程序,这个程序以浏览器作为界面,提供各种用户交互和呈现功能。
现在我们再回到#1.4所说的问题中来,传统的HTML网页中,如果网页需要更新,就必须整个页面全部重新加载。这种情况第一是浪费带宽、二是用户体验不佳,最重要的是极大地限制了基于web提供业务功能的灵活性。
传统的HTML网页的局限性,可以简单地理解为是协议的局限性,因为根本没有定义怎么样可以不全部刷新,或是只刷新部分页面。用户只有一个发出http请求的地方——那就是地址栏。
改变是由Javascript带来的。既然浏览器端可以从服务器端下载程序来运行了,为什么不能让程序来发起一个特殊的HTTP请求呢?让程序根据用户的交互来发起一个特别的HTTP请求,让程序来控制刷新一小部分页面并加载新内容,而不是等到整个页面全部刷新。这就是异步刷新的概念。
异步加载有一个挺酷的名字,AJAX(Asynchronous JavaScript and XML)。通过在后台与服务器进行少量数据交换,AJAX 可以使网页实现异步更新。这意味着可以在不重新加载整个网页的情况下,对网页的某部分进行更新。
Javascript为此定义了一种XMLHttpRequest对象,包含了进行异步加载的函数功能。看个例子:
<html>
<head>
<script type="text/javascript">
function loadXMLDoc()
{
var xmlhttp;
if (window.XMLHttpRequest)
{// code for IE7+, Firefox, Chrome, Opera, Safari
xmlhttp=new XMLHttpRequest();
}
else
{// code for IE6, IE5
xmlhttp=new ActiveXObject("Microsoft.XMLHTTP");
}
xmlhttp.onreadystatechange=function()
{
if (xmlhttp.readyState==4 && xmlhttp.status==200)
{
document.getElementById("myDiv").innerHTML = xmlhttp.responseText;
}
}
xmlhttp.open("GET","/ajax/demo_get.asp?t=" + Math.random(),true);
xmlhttp.send();
}
</script>
</head>
<body>
<h2>AJAX</h2>
<button type="button" onclick="loadXMLDoc()">请求数据</button>
<div id="myDiv"></div>
</body>
</html>
在HTML部分,简单地定义了一个按钮,onclick事件触发loadXMLDoc()
函数。另外有一个空白的div,用于加载异步读取的内容。
在Javascript部分定义了loadXMLDoc()
函数。首先,创建一个新的XMLHttpRequest对象。接着定义了该对象onreadystatechange事件的响应操作,即当异步加载数据成功以后的操作,是在myDiv
这个div里面把加载的内容显示出来。
最后则是调用XMLHttpRequest的open和send方法,创建一个get请求并发送出去。
因为Javascript发出这个http请求之后,需要等待服务器的响应,等待的时间取决于网络传送的条件,是不可知的。所以后续的代码是不能顺序执行下去的,必须等待接收到响应以后,才能继续执行。因此需要等待,触发响应事件,再调用需要执行的代码,这称之为回调函数。