JavaScript高级编程II

     原文地址: http://www.onlamp.com/pub/a/onlamp/2007/08/23/advanced-javascript-ii.html?page=1

        在前面的文章中,我们介绍了两类JavaScript小工具及其源代码:浮动文本和弹出菜单。本文中,我们将继续介绍另外几个实用的JavaScript小工具,并着重说明其工作原理,因此你能够简单改动后应用到自己的程序中。本文中的JavaScript代码应该不用做不论什么改动就能够在当前全部主流浏览器上执行。所以,不用再费周折……
        使用div标签的图片切换(Image Toggling with the div Tag)
        非常多情况下,你可能想要把网页上的一类东西放在一起,比如图片,可是它们又会占领太多的空间。为什么不仅仅放置一张图片,而让用户使用一个开关在图片之间切换呢?例如以下:
figure
        事实上,这非常easy。注意,浏览器在每次切换时并没有去请求server,切换是马上完毕。窍门呢?把它们所有放到HTML中,可是每次仅仅显示一个。这个效果能够使用div标签及其样式轻松完毕。使用这个技术能够实现非常多令人印象深刻的效果,接下来的样例将会看到。可是首先,我们还是了解一下这个样例是怎样工作的。使用到的JavaScript函数仅仅有一个:        
function ShowImage(page, tag)
{
    
var i = 1;
    
var el;
    
while (el = document.getElementById(tag + i)) {
        
if (i == page)
            el.style.display 
= 'block';
        
else
            el.style.display 
= 'none';
        i
++;
    }

}
          实际的图片被放在HTML中它们应该出现的位置上,每个都有一个div标签包含,例如以下:
<table>
    
<tr valign="top">
        
<td>
            
<div style="display:block" id="image1">
                
<img src="/onlamp/2007/08/23/graphics/pic1.jpg" />
            
</div>
            
<div style="display:none" id="image2">
                
<img src="/onlamp/2007/08/23/graphics/pic2.jpg" />
            
</div>
            
<div style="display:none" id="image3">
                
<img src="/onlamp/2007/08/23/graphics/pic3.jpg" />
            
</div>
        
</td>
        
<td width="100%" align="right">
            
<select onchange="ShowImage(parseInt(this.value), 'image');">
                
<option selected="selected" value="1">Image 1</option>
                
<option value="2">Image 2</option>
                
<option value="3">Image 3</option>
            
</select>
        
</td>
    
</tr>
</table>
         这个table的结构仅仅是为了布局,所以依据本文讨论的目的能够忽略。重要的是,表格的第一个单元格中的三个div标签。注意,第一个div标签的display设置为了block,而其它几个设置为了none。不论什么display被设为none的内容被隐藏了,也就不会在浏览器窗体中绘制,虽然已经从server上取得,而且放到了页面上。因此,刚開始我们仅仅看到pic1.jp,而没有看到另外两张。
        然后,我们在下拉框的onchange事件发生时调用上面JavaScript函数。当用户在下拉框选择新的项目时就会触发该事件。我们传递两个參数:下拉框本身的引用和包括了全部图片的div标签id基础部分(注意image1、image2和image3都是以image开头)。
         函数先获得表示哪一个图片须要显示的选择值。parseInt用来保证保存的是数字而不是字符串。接下来获得第一个div标签的句柄,本例中是id为image1的div。假设这个图片须要显示,就将它的display样式设置为block,其它的设置为none。然后,接着处理下一个标签(image2),等等,直到没有了须要处理的标签,这是退出函数。
        因此,仅仅要下拉框中的新选项被选择,其对应的div区域就会显示,而其它的被隐藏。它们在网页上共享同一块区域,由于在HTML中它们一个个紧挨着的。然而,这当然不是一个限制,假设须要我们能够让它们穿过不同的区域,也能够依据不同的选择出如今网页上不同的位置。类似地,我们也没有限制仅仅能是图片;仅仅要是能够放在div标签中的不论什么东西都能够使用鼠标点击显示或隐藏。这个功能使你能够在页面放置很多其它的信息,而不须要用户拖动滚动栏。
        tab:使用div的很多其它乐趣(Tabs: More Fun with div)
        接下来我们看使用div标签的创造性作用的另外一个样例。有时候,须要在页面包括几个tab页。点击一个tab页面,就会显示其包括的信息,隐藏其它的,非常像当前一系列的tab类型的web浏览器(译者注:Firefox和IE7都是这样的形式)。可是,通常情况下,点击HTML的tab会引起页面的重载,这肯定会打断界面的连贯性。有没有信息能够马上呈现的方法呢?的确要感谢我们在上个样例中学到的小技巧,它能够完毕。试一下吧:
  • Summary
  • Details
  • Known Issues

Introducing the new, improved multi-widget. It slices, it dices, it even does your taxes! Order yours today! Call now: 555-WIDG


         没有什么值得惊奇的,这个样例使用了和上一个全然一样的JavaScript函数,可是实现了一个全然不同的效果。其余的效果有几条简单CSS规则实现。我们先看一下样例中的HTML代码:
<div style="display:block" id="tab1">
    
<ul class="tab">
        
<li class="tab_selected" onclick="ShowImage(1, 'tab');">
            Summary
        
</li>
        
<li onclick="ShowImage(2, 'tab');">
            Details
        
</li>
        
<li onclick="ShowImage(3, 'tab');">
            Known Issues
        
</li>
    
</ul>
    
<p>
        Introducing the new, improved multi-widget.  It slices, it dices, it even does
        your taxes!  Order yours today!  Call now: 555-WIDG
    
</p>
</div>

<div style="display:none" id="tab2">
    
<ul class="tab">
        
<li onclick="ShowImage(1, 'tab');">
            Summary
        
</li>
        
<li class="tab_selected" onclick="ShowImage(2, 'tab');">
            Details
        
</li>
        
<li onclick="ShowImage(3, 'tab');">
            Known Issues
        
</li>
    
</ul>
    
<p>
        The multi-widget is a sophisticated piece of complex machinery designed by the
        country's leading nuclear physicists.  Order yours today and you will quickly
        learn how easy it is to do just about anything in no time, thanks to our patented
        EZ-Widge technology.
    
</p>
    
<p>
        Motor: 5HP
<br />
        Dimensions: 8" x 5" x 2"
<br />
        Weight: 212 g
<br />
        Radioactivity: negligible
    
</p>
</div>

<div style="display:none" id="tab3">
    
<ul class="tab">
        
<li onclick="ShowImage(1, 'tab');">
            Summary
        
</li>
        
<li onclick="ShowImage(2, 'tab');">
            Details
        
</li>
        
<li class="tab_selected" onclick="ShowImage(3, 'tab');">
            Known Issues
        
</li>
    
</ul>

    
<ul>
        
<li>Do not use multi-widget near open flames</li>
        
<li>Do not run while holding multi-widget</li>
        
<li>Do not taunt multi-widget</li>
        
<li>
            Multi-widget may, under certain as yet undetermined circumstances,
            spontaneously explode.  We hereby disclaim any libaility for personal injury
            caused as a result of multi-widget; for your safety, we recommend wearing
            body armor while handling multi-widget.
        
</li>
    
</ul>
</div>
        注意,这里有三个div区域(每个是一个tab),就像上一个样例中每个图片有一个div。再次说明,第一个div被赋予display:block,而其它是display:none,所以刚開始仅仅有第一个div可见。这些区域每个都是先绘制三个tab,选中的tab和其它的颜色不同。因此,我们实际上在每个tab上都又一次绘制了三个tab。tab的内容能够是不论什么HTML。注意,内容仅仅出现一次,仅仅只是tab的HTML反复了多次,因此“浪费”的空间是最小的。
        每个tab在触发onclick事件时就会调用JavaScript函数ShowImage。就像我们在第一个样例中看到的,这个函数隐藏了除作为參数传递给ShowImage的div之外的其它div区域。因此,我们在这里使用显示点击的tab,隐藏其它的。CSS或者样式用来实际绘制tab。我们定义了两个类:tab和tab_selected。前者应用于整个tab栏,而后者仅仅用于被选择的tab。CSS的代码例如以下:       
ul.tab {
    margin
: 0;
    padding
: 3px 0;
    border-bottom
: 1px solid #778;
    font-weight
: bold;
}


ul.tab li 
{
    display
: inline;
    padding
: 3px 0.5em;
    margin-left
: 3px;
    border-top
: 1px solid #778;
    border-left
: 1px solid #778;
    border-right
: 1px solid #778;
    border-bottom
: none;
    background
: top repeat-x #89aac7;
    white-space
: nowrap;
    color
: white;
    cursor
:pointer;
}


ul.tab li.tab_selected 
{
    background
: #fff;
    border-bottom
: 1px solid #fff;
    color
: black;
}
        第一部分用于ul标签,也就是整个tab栏。它设置margin和padding属性,指定全部tab靠左排列,并在tab栏的以下绘制一条边线。要让tab排成合适的队列,padding属性是必须设置的。另外,我们把tab栏中的文本加粗。
        接下来的部分用在tab栏中的全部li标签上。display:inline可能是最重要的,它让列表横向排列取代纵向。还是margin和padding属性让tab排成一行,而且在相邻tab之间留下一定空隙。border属性设置tab的顶部和两边的边线(底部的边线来自ul标签)。最后,设置背景和前景色,确保文字不会换行。
        最后一部分之应用于当前选中的tab,覆盖了第二部分中的一些属性设置。改变了tab的背景和前景色,使其突出,同一时候使用白色的底线掩盖了ul标签的黑色底线。这样就有了选中的tab在其它tab之前的感觉。
        在本例中,tab的全部被载入后保存在网页上,所以点击一个tab时差点儿是马上呈现其内容,不会引起页面的不论什么重载。使用一个十分简单的JavaScript函数和一小段CSS,我们就得到了真正令人印象深刻的效果。
        div得很多其它技巧(And Even More div  Tricks)
        和上一个样例中我们学到实用的应用程序的技巧一样,尝试以下的样例,能够用来作为文本或列表的总结性区域,同意用户仅仅展开和查看他/她感兴趣的内容:           
Click to Expand Choice of four widget colors
         源码例如以下:
<div style="display:block;" id="colors1">
    
<table style="background:#eeeebb">
        
<tr>
            
<td>
                
<img src="/onlamp/2007/08/23/graphics/expand.jpg" style="cursor:pointer;"
                alt
="Click to Expand" title="Click to Expand" onclick="ShowImage(2, 'colors');" />
            
</td>
            
<td>
                Choice of four widget colors
            
</td>
        
</tr>
    
</table>
</div>
<div style="display:none;" id="colors2">
    
<table style="background:#eeeebb">
        
<tr valign="top">
            
<td>
                
<img src="/onlamp/2007/08/23/graphics/collapse.jpg" style="cursor:pointer;"
                alt
="Click to Collapse" title="Click to Collapse" onclick="ShowImage(1, 'colors');" />
            
</td>
            
<td>
                
<ul>
                    
<li>blue</li>
                    
<li>green</li>
                    
<li>red</li>
                    
<li>brown</li>
                
</ul>
            
</td>
        
</tr>
    
</table>
</div>
       现在,这个效果是怎样完毕的应该相当清楚了。再说一次,这里有两个div块,一个是折叠样式的文本和展开button,还有一个是展开样式的文本和折叠button。最初,我们仅仅看到折叠样式(display:block),接着我们使用图片的onclick回调在两个div之间切换。两个div的id分别为colors1colors2,这样就便于使用我们前面编写的JavaScript函数ShowImage
       拖拽和切换(Drag and Drop and Swap)
       现在,我已经过多的分析和讨论了div标签,以下我们就来看一些不同的东西。在这个样例中,我们展示一个使用鼠标拖拽文本(或图片)的方法。这也能够用来实现JavaScript游戏或者更加重要的目的,比如,同意你更换页面中图片的顺序。在以下的样例中,尝试拖拽三个名字来更换它们的位置。注意,你能够没有不论什么问题的交换John和Jane,可是却不能交换Bill和另外两个的位置,由于他总是捣乱。
John
Jane
Bill
(译者注:因为blog限制,不能在本页面中演示其效果,能够通过顶部链接进入原文查看其执行结果)
        这个样例比至今为止的其它样例包括很多其它的代码,可是它仍然是相当易于理解的。和其它样例不同,它使用了全局鼠标事件。特别指出,它依赖于三个回调函数:按下鼠标、移动鼠标和释放鼠标。使用这些类别的全局事件缺点是,假设一次触发多个事件,就会变得很危急。所以,你应该最小化这些事件的同一时候使用,在每一个页面最多使用一个或者两个。对于这样一个特别样例,全局事件是很必要的。以下我们每一次仅仅看一部分代码:
// Set the callbacks
document.onmousedown = mousedown;
document.onmousemove 
= movemouse;
document.onmouseup   
= mouseup;

var lastobj;  // Last draggable object we hovered over
var isdrag;   // True if dragging an object
        此处,我们初始化三个回调函数。mousedown在用户点击鼠标时调用,movemouse在移动鼠标时调用,而mouseup在释放鼠标左键时被调用。我们还须要两个全局变量,第一个跟踪我们近期悬停的对象,第二个是一个标志,表示我们当前是否正在拖动一个对象。        
// This prevents browsers from highlighting the draggable text
//
 when you click on it.  The table containing all the draggable
//
 text has id drag_drop.

window.onload 
= function()
{
    
var e = document.getElementById('drag_drop');
    
if (e) {
        
if (moz)
            e.onmousedown 
= function () return false; } // mozilla
        else
            e.onselectstart 
= function () return false; } // ie
    }

}
        这个短小的函数在页面一装载时就运行,它确保点击可拖拽对象时,文本不会高亮,就好像点击正常的文本一样。实现原理是使用一个空函数重写了负责高亮显示文本的函数。       
// Checks to see if a swap is allowed between two objects based on their ids.
//
 Change this as you see fit to permit or forbid swapping each possible pair
//
 of draggable items.
function allowswap(a,b)
{
    
if (a.id == "dragdropa" && b.id == "dragdropb" || a.id == "dragdropb" && b.id == "dragdropa")
        
return true;
    
return false;
}


// Returns true if an object is draggable - change this to suit your needs.
function isdraggable(obj)
{
    
if (obj.id.substr(0,8== "dragdrop")
        
return true;
    
return false;
}
         这里是两个我们须要的工具函数;输入两个对象,假设它们能够交换位置,allowswap则返回true,否则返回false。非常明显,在这个位置使用一些颇具奥妙的逻辑,你就能够完毕一些有趣的事情。假设输入的对象是能够拖拽的,isdraggable返回true。依据本例的目的,我们仅仅推断iddragdrop开头的对象,当然,你能够依据须要改变。可拖拽性也能够使用其它方式表示,比如一个特别的类(class),可是这里基于我们样例的目的。        
// Callback when mouse button is pressed.  This checks if an item is draggable, and
//
 if so initiates the process.
function mousedown(e) 
{
    
var obj = moz ? e.target : event.srcElement;
    
// Trace up DOM tree to see if item clicked on is draggable.  This allows
    // for the fact that you may click, for example, on a TD while the enclosing
    // TR is the draggable object.
    while (obj.tagName != "HTML" && obj.tagName != "BODY" && !isdraggable(obj)) {
        obj 
= moz ? obj.parentNode : obj.parentElement;
    }

    
if (isdraggable(obj)) {
        
// If draggable, set a global flag to track this, and save a pointer
        // to the object in a global variable as well (dragobj).
        isdrag = true;
        dragobj 
= obj;

        
// origx, origy is original starting location of dragged object
        origx = dragobj.style.left;
        origy 
= dragobj.style.top;

        
// x,y is absolute co-ordinates within the window
        x = moz ? e.clientX : event.clientX;
        y 
= moz ? e.clientY : event.clientY;

        
// While offsetX, offSetY depend on where exactly you clicked on the object.
        // Thus if you click in the middle of the object, it will be 'attached' to
        // the mouse at that point, and not the upper left corner, for example.
        offsetX = moz ? e.layerX + 2: event.x + 2;
        offsetY 
= moz ? e.layerY + 2: event.y + 2;
    }

}
         鼠标按下时调用mousedown,这是这段代码里的第一个基本的部分。那么,当点击某个对象时,我们要检查它能否够拖拽。如果不能够,则检查其父对象能否够拖拽等等。这是由于当我们点击某个对象时,浏览器往往返回DOM树中最底层、最深层的对象。那么比如,有一个包括了p标签并能够拖拽的span标签,点击p标签中的文字,你将会得到p标签的句柄,可是它并不可拖拽。然而检查其父对象,我们将会发现作为一个可拖拽对象的子对象,它实际上是能够拖拽 的。
        如果我们点击了一个可拖拽对象,我们要将全局标志设为true,把对象保存在dragobj中。我们还要保存几个坐标:dragobj的原始位置、点击时的鼠标相对位置和点击的绝对位置。       
// Callback when mouse is moved.  It will change the cursor when you move over an object
//
 you can - or cannot - swap with.

function movemouse(e)
{
    
// Check if we are dragging an object
    if (isdrag) {
        
// If so, set the dragged object's position relative to how much the mouse has moved
        // since first clicked.
        dragobj.style.left = moz ? origx + e.clientX - x + offsetX + 'px' : origx + event.clientX - x + offsetX;
        dragobj.style.top  
= moz ? origy + e.clientY - y + offsetY + 'px' : origy + event.clientY - y + offsetY;
        
var obj = moz ? e.target : event.srcElement;
    
        
// If we are over an element that we cannot swap with, change its cursor style
        // to show that a swap is forbidden
        if (obj != dragobj && isdraggable(obj) && !allowswap(dragobj,obj)) {
            obj.style.cursor 
= 'wait';
            
// save in a handle to the object in a global so we can reset the cursor later
            lastobj = obj;
        }

        
else if (lastobj)  // reset the cursor as soon as we move off a non-swappable object
            lastobj.style.cursor = 'pointer';
        
return false;
    }

    
else {
        
// Sometimes can get stuck with no drop icon, so restore cursor just to be safe,
        // when not dragging but passing over a draggable item
        var obj = moz ? e.target : event.srcElement;
        
if (isdraggable(obj))
            obj.style.cursor 
= 'pointer';
    }

}
        仅仅要移动鼠标,我们就要首先检查是否有对象被拖动。假设是,我们要计算对象应该被拖拽到的位置(毕竟,它自己不会像被施了魔法一样尾随着鼠标),然后移动对象到对应位置。这个公式主要保证从对象被点击開始,它的移动就要和鼠标的移动保持一致。我们还须要检查是否正悬停在另外一个可拖拽对象上。若是,我们使用allowswap确定能否够二者能否够交换位置。假设不能够,就把鼠标指针设为“等待”的形状(可能会由于电脑的不同而呈现不同的形状,可是这里是标准的忙碌指针)。假设能够交换位置或者并非在一个可拖拽对象上,我们就无论鼠标指针的形状,假设曾经改变过就把它恢复原状。
        注意,作为额外预防措施,假设没有拖拽不论什么对象将鼠标悬停在可拖拽对象上时,我们仍然将鼠标指针恢复到正常状态。这是由于有时鼠标指针会停留在“等待”状态,也要依赖于用户移动鼠标、释放鼠标按键有多高速,等等。这就是说,上面提到恢复鼠标指针状态的逻辑不是没有道理的。
// Callback when mouse is released - checks if a swap should occur and
//
 returns dragged object to its starting position if not.

function mouseup(e)
{
    
if (isdrag) {  // If something is being dragged

        
// Get the object over which the mouse button was just released
        var obj = moz ? e.target : event.srcElement;

        
// Check if mouse was release over an object we can swap with
        if (obj != dragobj && isdraggable(obj) && allowswap(dragobj, obj)) {

            
// A swap is allowed - swap color, tooltip and contents of the
            // dragged object with that it was released over
            var htm = obj.innerHTML;
            obj.innerHTML 
= dragobj.innerHTML;
            dragobj.innerHTML 
= htm;

            
var col = obj.style.color;
            obj.style.color 
= dragobj.style.color;
            dragobj.style.color 
= col;

            
var titl = obj.title;
            obj.title 
= dragobj.title;
            dragobj.title 
= titl;

            
// Set the position of the object we were dragging (dragobj) where the
            // other object (obj) is located and move obj to the original location
            // of dragobj before it was dragged (origx, origy).
            dragobj.style.left = obj.style.left;
            dragobj.style.top 
= obj.style.top;
            obj.style.left 
= origx;
            obj.style.top 
= origy;
        }

        
else {
            
// No swap can occur so return dragged object to its starting position
            dragobj.style.left = origx;
            dragobj.style.top 
= origy;
        }


        
// Restore cursor to pointer if it was changed in movemouse above
        if (lastobj) {
            lastobj.style.cursor 
= 'pointer';
        }

    }

    isdrag 
= false;  // Nothing is being dragged now
}
        假设在拖拽一个对象时释放鼠标,mouseup要做最多的外勤工作。假设在一个可拖拽对象上释放鼠标,我们使用allowswap推断能否够交换位置。假设能够,我们交换两个对象的文本内容、颜色和提示(title属性)。此时,你可能更愿意交换它们的全部属性,依目的而定。然后,我们移动被拖拽的对象到在其上释放鼠标的对象的位置,然后把该对象移动到被拖拽对象的原始位置。如此,就完毕了位置交换。还有一方面,假设不同意交换,我们仅仅须要把被拖拽的对象移动到它開始的位置。不管如何,假设在上面的movemouse中我们把鼠标指针改为了“等待”状态,就要回复正常情况,同一时候,我们要清除isdrag标志,表示当前没有拖拽不论什么对象。
        最后,初始化对象很easy。仅仅须要记住,可拖拽对象的id是由dragdrop开头的:
<table id="drag_drop" align="center" style="font-size:150%; color:green; background-color:#88ccff; white-space: nowrap;">
    
<tr>
        
<td>
            
<span id="dragdropa" style="cursor: pointer; position: relative; color: #ff0000" title="John Doe">John</span>
        
</td>
    
</tr>
    
<tr>
        
<td>
            
<span id="dragdropb" style="cursor: pointer; position: relative; color: #a0522d" title="Jane Smith">Jane</span>
        
</td>
    
</tr>
    
<tr>
        
<td>
            
<span id="dragdropc" style="cursor: pointer; position: relative; color: #00aa00" title="Bill Schwartz">Bill</span>
        
</td>
    
</tr>
</table>
       放在表格中的对象,为了方便又使用不同的颜色表示,可是唯一重要的东西是可拖拽对象的id(它们在allowswap中被引用)以及可拖拽对象的style属性中的position:relative。这个规则在JavaScript中惯于使用对象的相对定位计算它们的位置。还须要注意,外层的表格的id是drag_drop,在刚開始的window.onload函数中引用。
        总结
        但愿前面的样例已经向你展示了div的一些创造性方式,能够用来分割或选择性的展示网页的部分内容。拖拽的样例仅仅是十分强大的概念的一个基础样例,在今天多数的高级网页上都能够见到。仅仅需再做一点工作,它就能够用来实现一个全然的可定制主页,比如,你能够拖动和交换新闻feed、图像和很多其它你内心的内容。唯一的限制,就是你的想象力!
posted @ 2014-07-09 20:46  hrhguanli  阅读(213)  评论(0编辑  收藏  举报