65.9K
CodeProject 正在变化。 阅读更多。
Home

ASP.NET 开发者 JavaScript 技巧 - 第 2 部分

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.75/5 (21投票s)

2007年9月18日

CPOL

8分钟阅读

viewsIcon

83494

downloadIcon

397

ASP.NET 开发者 JavaScript 技巧

引言

本文是为 ASP.NET 开发者提供 JavaScript 技巧系列文章的第二部分。您也可以阅读第一部分

背景

毫无疑问,ASP.NET 服务器控件使您的服务器端开发变得轻松。像 Karamasoft UISuite™ 这样的第三方组件使服务器端和客户端开发都变得更加容易。但是,您可能仍然需要一些客户端编程知识,才能为您的网站访问者提供强大的用户界面。

无论您的服务器端开发环境如何,JavaScript 都是最常用的客户端编程语言。尽管本文可能适用于其他语言和框架,但它更适合 ASP.NET 开发者,因为大多数技巧都附有 ASPX 和 C# 示例代码。

Using the Code

  1. 缓存您的脚本
  2. 获取内部文本
  3. 切换显示
  4. 预加载您的图像
  5. 高效地拼接字符串
  6. 使用哈希表进行高效查找
  7. 检查数组中是否存在某项
  8. 检索查询字符串值
  9. 在 change 事件后将焦点从下拉列表中移开
  10. 用最小增长数组处理异步操作
  11. 使用 post 从一个页面传递数据到另一个页面

12. 缓存您的脚本

与直接将 JavaScript 代码嵌入脚本块相比,使用 JavaScript 包含文件具有几个优点。主要优点是它通过将代码集中存放来实现代码重用。另一个优点是您的页面大小将显著减小,使页面更易于维护。可能最重要的优点是浏览器的缓存。对您的页面的后续请求将从浏览器缓存中检索 JavaScript 库,以便更快地加载。

您可以将 JavaScript 代码放在 *.js 文件中,并使用以下语法将其包含在您的网页中

<script src="KaramasoftUtility.js" type="text/javascript"></script>

13. 获取内部文本

Internet Explorer 提供 innerText 属性来获取或设置对象开始标签和结束标签之间的文本。Firefox 不提供 innerText 属性,但提供 textContent 属性来获取或设置内部文本。对于不提供获取或设置内部文本属性的浏览器,您可以清除 innerHTML 属性中的 HTML 标签。

function GetInnerText(elem) {
 return (typeof(elem.innerText) != 'undefined') ?
    elem.innerText : (typeof(elem.textContent) != 'undefined') ?
    elem.textContent : elem.innerHTML.replace(/<[^>]+>/g, '');
}

14. 切换显示

您可以轻松地通过在 'none''' 之间切换 HTML 元素的样式 display 值来切换其显示。

JavaScript

function ToggleDisplay(elemId) {
 var elem = document.getElementById(elemId);
 elem.style.display = (elem.style.display != 'none') ? 'none' : '';
}

ASPX

<asp:LinkButton ID="lbToggleDisplay" runat="server"
    OnClientClick="ToggleDisplay('pnlToggleDisplay');
    return false;">Toggle Display</asp:LinkButton>
<asp:Panel ID="pnlToggleDisplay" runat="server">
Panel content here!
</asp:Panel>

15. 预加载您的图像

如果您想在页面加载时立即显示您的图像,您应该在网页的 HEAD 部分使用 JavaScript 预加载您的图像。当您在图像上设置鼠标悬停效果(即鼠标悬停时显示不同的图像)时,此技术也很有帮助。

您只需创建一个新的 JavaScript Image 对象,并将其 src 属性设置为您的图像路径。

<head runat="server">
<script type="text/javascript">
var img1 = new Image();
img1.src = '/Images/Product/UISuite.gif';
</script>
</head>

请注意,您需要为所有需要缓存的图像创建新的 JavaScript Image 对象,并使用不同的局部变量名称。为了解决这个问题,您可以使用以下 JavaScript 函数,它将图像路径作为参数,并将 JavaScript Image 对象存储在数组元素中。

<head runat="server">
<script type="text/javascript">
function PreloadImages() {
 var lenArg = arguments.length;
 if (lenArg > 0) {
  var imgArr = new Array();
  for (var i = 0; i < lenArg; i++) {
   imgArr[i] = new Image();
   imgArr[i].src = arguments[i];
  }
 }
}
PreloadImages('/Images/Product/UltimateEditor.gif',
    '/Images/Product/UltimateSearch.gif', '/Images/Product/UltimateSpell.gif');
</script>
</head>

此函数接受您传递的任意数量的图像路径参数,为每个参数创建一个新的 JavaScript Image 对象,并将它们存储在一个数组中。

16. 高效地拼接字符串

尽管使用 + 运算符连接字符串很容易,但当字符串变大并且需要连接大量字符串时,字符串操作在 CPU 周期方面变得非常昂贵。

更好的替代方法是创建您自己的 StringBuilder JavaScript 对象,该对象类似于 .NET Framework 中的 StringBuilder 对象。您可以定义您的 StringBuilder 对象,它具有一个缓冲区数组属性,用于存储要连接的字符串。然后添加一个 Append 方法来将字符串添加到缓冲区数组。JavaScript Array 对象提供了一个 join 方法,该方法将数组的所有元素放入一个由指定分隔符分隔的字符串中。ConvertToString 方法将连接缓冲区数组的所有元素为一个字符串。

function StringBuilder() {
 this.buffer = [];
}
StringBuilder.prototype.Append = function(str) {
 this.buffer[this.buffer.length] = str;
};
StringBuilder.prototype.ConvertToString = function() {
 return this.buffer.join('');
};

而不是以下代码

var str = 'This ' + 'is ' + 'a ' + 'test';

您现在可以使用以下代码

var sb = new StringBuilder;
sb.Append('This ');
sb.Append('is ');
sb.Append('a ');
sb.Append('test');
var str = sb.ConvertToString(); 

我们的测试表明,在 Internet Explorer 中,使用 StringBuilder Append 方法连接字符串 'test' 50,000 次,比使用 + 运算符快 10 倍。

17. 使用哈希表进行高效查找

哈希表提供了一种高效添加新条目的方式,并且搜索指定键所需的时间与存储的条目数量无关,即 O(1)。在解释如何在 JavaScript 中创建哈希表之前,让我们快速了解一下 JavaScript 中的关联数组。

关联数组(哈希)是一种抽象数据类型,由键的集合和值的集合组成,其中每个键都与一个值关联。在 JavaScript 中,所有对象都是关联数组。您可以按如下方式访问对象属性

document.forms['Form1']

document.forms.Form1

JavaScript 对象不提供像 Array 对象那样的 length 属性,但您可以使用以下结构遍历对象属性

for (var prop in object)

例如,UltimateEditor 组件在客户端 API 中提供了一个名为 UltimateEditors 的关联数组。每个 UltimateEditor 客户端对象都被定义为 UltimateEditors 关联数组的属性。如果您的页面中有多个 UltimateEditor 组件,以下 JavaScript 代码将遍历所有 UltimateEditor 对象并显示它们的 HTML 内容

for (var ueObj in UltimateEditors) {
 alert(ueObj.GetEditorHTML());
}

我们现在可以使用关联数组功能创建我们的 HashTable 对象。我们的 HashTable 对象将有两个属性:存储键/值对的 hashArr 属性和跟踪哈希表中项目数量的 length 属性。它将提供四个方法

  1. get 方法用于检索给定键的值
  2. put 方法用于向哈希表添加键/值对
  3. remove 方法用于从哈希表中删除键/值对
  4. has 方法用于返回给定键值是否存在于哈希表中
function HashTable(){
    this.hashArr = new Array();
    this.length = 0;
}

HashTable.prototype.get = function(key) {
    return this.hashArr[key];
};

HashTable.prototype.put = function(key, value) {
 if (typeof(this.hashArr[key]) == 'undefined') {
  this.length++;
 }
    this.hashArr[key] = value;
};

HashTable.prototype.remove = function(key) {
 if (typeof(this.hashArr[key]) != 'undefined') {
  this.length--;
  var value = this.hashArr[key];
  delete this.hashArr[key];
  return value;
 }
};

HashTable.prototype.has = function(key) {
 return (typeof(this.hashArr[key]) != 'undefined');
};

现在让我们为高效的电话查找创建一个示例哈希表

var phoneLookup = new HashTable();
phoneLookup.put('Jane', '111-222-3333');
phoneLookup.put('John', '444-555-6666');
alert(phoneLookup.get('Jane'));

18. 检查数组中是否存在某项

JavaScript Array 对象不提供内置方法来返回给定值是否存在于数组中。但是,我们可以通过添加一个方法来扩展 Array 功能,该方法返回数组是否具有给定值。

Array.prototype.has = function(value) {
 var i;
 for (var i = 0, loopCnt = this.length; i < loopCnt; i++) {
  if (this[i] === value) {
   return true;
  }
 }
 return false;
};

请注意,此方法使用 === 运算符检查值是否与数组中的值完全相同(相等且类型相同)。如果您只想检查它们是否相等,则应将 === 替换为 == 运算符(相等)。

然后,我们可以按如下方式使用此方法

var arr = new Array();
arr[0] = 'test';
alert(arr.has('test')); // Should display true
alert(arr.has('test2')); // Should display false

19. 检索查询字符串值

ASP.NET 提供 Request.QueryString 集合,用于检索 HTTP 查询字符串中变量的值,但客户端没有内置机制来检索查询字符串集合。但是,您可以使用我们之前提供的 GetAttributeValue 方法来解析窗口位置搜索属性以获取查询字符串值,该方法用于将您的窗口居中。请注意,window.location.search 属性获取或设置 href 属性中问号后面的子字符串。

function GetAttributeValue(attribList, attribName, firstDelim, secondDelim) {
 var attribNameLowerCase = attribName.toLowerCase();
 if (attribList) {
  var attribArr = attribList.split(firstDelim);
  for (var i = 0, loopCnt = attribArr.length; i < loopCnt; i++) {
   var nameValueArr = attribArr[i].split(secondDelim);
   for (var j = 0, loopCnt2 = nameValueArr.length; j < loopCnt2; j++) {
    if (nameValueArr[0].toLowerCase().replace(/\s/g, '') ==
            attribNameLowerCase && loopCnt2 > 1) {
     return nameValueArr[1];
    }
   }
  }
 }
} 

此方法接受三个参数:一个名称/值对列表,要检索其值的属性名称,第一个分隔符和第二个分隔符。第一个分隔符将是 & 符号,第二个分隔符将是等号,用于解析查询字符串变量。然后您可以使用以下方法检索给定查询字符串变量名称的查询字符串值。

function GetQueryStringValue(varName) {
 return GetAttributeValue
    (window.location.search.substring(1), varName, '&', '=');
}

20. 在 Change 事件后将焦点从下拉列表中移开

当用户更改 DropDownList 中的选择时,焦点仍保留在下拉列表中。如果用户按下上/下箭头键或移动鼠标滚轮,该操作将触发下拉列表上的 onchange 事件。

您可以通过在 onchange 事件中使下拉列表失去焦点来防止这种情况。

JavaScript

function LoseFocus(ddlElem) {
 ddlElem.blur();
}

ASPX

<asp:DropDownList id="ddlLoseFocus" runat="server" AutoPostBack="true" />

C#

protected void Page_Load(object sender, EventArgs e)
{
ddlLoseFocus.Attributes.Add("onchange", "LoseFocus(this)");
}

21. 使用最小增长数组处理异步操作

当您使用异步 AJAX 回调时,您可能需要将回调结果存储在数组中以便以后处理。棘手的部分是使数组大小保持最小。为此,您可以在处理完数组项后将其清空,使其可用于未来的回调结果,并始终将新的回调结果插入到第一个可用的数组项中。

function InsertActiveCallbackArr(arrayObj, arrayElem) {
 var i = 0;
 for (var loopCnt = arrayObj.length; (i < loopCnt) && arrayObj[i]; i++);
 arrayObj[i] = arrayElem;
}

当您完成回调结果的处理时,只需将该数组项设置为 null

function ProcessActiveCallbackArr(arrayObj) {
 for (var i = 0, loopCnt = arrayObj.length; i < loopCnt; i++) {
  // Process the callback result here
  // Then set the array item to null
  arrayObj[i] = null;
 }
}

22. 使用 Post 从一个页面传递数据到另一个页面

如果您需要从一个页面向另一个页面传递少量数据,您可以在查询字符串中传递它,目标页面可以通过 Request.QueryString 集合(GET 操作)检索它。但是,如果您需要传递大量数据,您不能在查询字符串中传递它,因为 Internet Explorer 对页面地址有最大 2048 个字符的限制。在这些情况下,您可以使用 POST 操作从一个页面向另一个页面传递数据。在您的源页面中,定义一个隐藏变量来保存要传递的数据,并仅将其 ID 在查询字符串中传递给目标页面。

目标页面应首先通过 Request.QueryString 集合获取该 ID。然后,在 BODY onload 事件处理程序期间,它应该在客户端将其自己的隐藏变量值设置为源框架中的值。您可以在客户端使用 opener 对象检索 opener 页面中的 DOM 元素。然后它应该进行回发并检索其隐藏变量的值通过 Request.Form 集合。

源页面 JavaScript

function PassData(ctlID) {
 window.open('TargetPage.aspx?ctlID=' + ctlID, 'NewWindow', 'width=400,height=300,
    location=no,menubar=no,resizable=no,scrollbars=no,status=yes,toolbars=no');
}

源页面 ASPX

<asp:TextBox ID="txtPassData" runat="server" Text="Data to pass"
    CssClass="TextBox"></asp:TextBox>
<asp:Button ID="btnPassData" runat="server" Text="Pass Data"
    CssClass="Button" OnClientClick="PassData('txtPassData'); return false;" />

目标页面 JavaScript

function Body_Onload() {
    var pageStatusElem = document.getElementById('hfPageStatus');

    // Page status hidden field value is initially empty string
    if (pageStatusElem.value == '') {
        // Set text to spell check hidden field value to the
        // text in the opener window
        document.getElementById('hfTextPassed').value =
            opener.window.document.getElementById
            ('<%=Request.QueryString["ctlID"]%>').value;

        // Set page status hidden field value so that when the page is posted back
        // it can process text to spell check retrieved from the opener window
        pageStatusElem.value = '<%=SET_TEXT%>';

        // Post back the page
        document.form1.submit();
    }
} 

目标页面 ASPX

<body onload="Body_Onload()">
    <form id="form1" runat="server">
    <div>
    Text passed: <asp:Label ID="lblTextPassed" runat="server" Text="Label">
        </asp:Label>
    </div>
    <asp:HiddenField ID="hfTextPassed" runat="server" Value="" />
    <asp:HiddenField ID="hfPageStatus" runat="server" Value="" />
    </form>
</body>

目标页面 C#

public const string SET_TEXT = "SetText";
public const string TEXT_IS_SET = "TextIsSet";

protected void Page_Load(object sender, EventArgs e)
{
 // Initial entry
 if (hfPageStatus.Value == SET_TEXT)
 {
  // Set page status not to enter again
  hfPageStatus.Value = TEXT_IS_SET;
 }
 lblTextPassed.Text = hfTextPassed.Value;
}
© . All rights reserved.