首页 Javascript富客户端RIA系列之一:搭建JS框架类库

Javascript富客户端RIA系列之一:搭建JS框架类库

举报
开通vip

Javascript富客户端RIA系列之一:搭建JS框架类库Javascript富客户端RIA系列之一:搭建JS框架类库 经过了"面向对象的Javascript系列"的预热,让我们再次起航进入Javascript富客户端系列。基于链式调用关于类库的讲解,本讲将一步一步搭建一个属于自己的JS框架类库。在这里,不妨问问大家:一个类库怎样才能算有价值的类库呢,我想不妨从以下几个方面去考量: 1). 避免改变JS固有的基础对象。即如对JS对象Function,String,Array等,不要试图改变这些对象的行为来适应你的场景。 2). 具有良好的版本控制和文档注释。即JS类...

Javascript富客户端RIA系列之一:搭建JS框架类库
Javascript富客户端RIA系列之一:搭建JS框架类库 经过了"面向对象的Javascript系列"的预热,让我们再次起航进入Javascript富客户端系列。基于链式调用关于类库的讲解,本讲将一步一步搭建一个属于自己的JS框架类库。在这里,不妨问问大家:一个类库怎样才能算有价值的类库呢,我想不妨从以下几个方面去考量: 1). 避免改变JS固有的基础对象。即如对JS对象Function,String,Array等,不要试图改变这些对象的行为来适应你的场景。 2). 具有良好的版本控制和文档注释。即JS类库必须有详细的文档,以备使用者能更快的应用。 3). 具有 规范 编程规范下载gsp规范下载钢格栅规范下载警徽规范下载建设厅规范下载 命名空间的约定。即JS类库必须添加完整的命名空间,有利于开发者快速定位自己需要的功能。 4). 避免加入任何的业务代码。即不要将与你业务逻辑相关的代码添加到类库中。 5). 模块职责清晰,按需加载。即基础类库并不是一个超大库,而是按职责划分出来组合在一起的,在你需要的场景中仅加载所需要的类库。 6). ........... 可能还有很多,以上是一个优秀框架的基本特征。接下来我们开始搭建我们的JS框架类库。 1). 定义基础类库的类。 var $ = function() {}; 调用方式: // Instantiate the $ library object as a singleton for use on a page $ = new $(); 2). 重写window.onload事件。我们都知道一个标准的html包含和元素,通常JS开发人员习惯把所有的页面初始化代码放在window.onload中,但此事件是在整个页面(包含外部资源如图片,动画等)加载完毕后触发。这样对于大型的站点来说,它的图片量越大直接导致用户等待页面显示的事件越久。但幸运的是,W3C组织定义了"DOMContentLoaded"事件, 在页面元素加载完毕之后和页面外部资源文件加载之前触发,这样用户不必再浪费时间去等待图片全部加载完毕后才能看到页面。不幸的是,有些浏览器(如IE6,7,8)并未实现该事件。因此,我们需要重写window.onload事件来解决这个问 快递公司问题件快递公司问题件货款处理关于圆的周长面积重点题型关于解方程组的题及答案关于南海问题 。 $.prototype.onDomReady = function(callback) { if (document.addEventListener) { // If the browser supports the DOMContentLoaded event, // assign the callback function to execute when that event fires document.addEventListener('DOMContentLoaded', callback, false); } else { if(document.body && document.body.lastChild) { // If the DOM is available for access, execute the callback function callback(); } else { // Reexecute the current function, denoted by arguments.callee, // after waiting a brief nanosecond so as not to lock up the browser return setTimeout(arguments.callee, 0); } } } 调用方式: // Outputs "The DOM is ready!" when the DOM is ready for access $.onDomReady(function() { alert("The DOM is ready!"); }); 3). 统一多浏览器事件触发机制。大多数浏览器的事件触发机制都差不多,如超链接点击或 但在IE6和IE7以及其它浏览器却不相同,不仅Form提交。 如此,在页面元素和属性之间也有差异,如获取鼠标位置等。 // Add a new namespace to the $ library to hold all event-related code, // using an object literal notation to add multiple methods at once $.prototype.Events = { // The add method allows us to assign a function to execute when an // event of a specified type occurs on a specific element add: function(element, eventType, callback) { // Store the current value of this to use within subfunctions var self = this; eventType = eventType.toLowerCase(); if (element.addEventListener) { // If the W3C event listener method is available, use that element.addEventListener(eventType, function(e) { // Execute callback function, passing it a standardized version of // the event object, e. The standardize method is defined later callback(self.standardize(e)); }, false); } else if(element.attachEvent) { // Otherwise use the Internet Explorer-proprietary event handler element.attachEvent("on" + eventType, function() { // IE uses window.event to store the current event's properties callback(self.standardize(window.event)); }); } }, // The remove method allows us to remove previously assigned code from an event remove: function(element, eventType, callback) { eventType = eventType.toLowerCase(); if (element.removeEventListener) { // If the W3C-specified method is available, use that element.removeEventListener(element, eventType, callback); } else if (element.detachEvent) { // Otherwise, use the Internet Explorer-specific method element.detachEvent("on" + eventType, callback); } }, // The standardize method produces a unified set of event // properties, regardless of the browser standardize: function(event) { // These two methods, defined later, return the current position of the // mouse pointer, relative to the document as a whole, and relative to the // element the event occurred within var page = this.getMousePositionRelativeToDocument(event); var offset = this.getMousePositionOffset(event); // Let's stop events from firing on element nodes above the current if(event.stopPropagation) { event.stopPropagation(); } else { event.cancelBubble = true; } // We return an object literal containing seven properties and one method return { // The target is the element the event occurred on target: this.getTarget(event), // The relatedTarget is the element the event was listening for, // which can be different from the target if the event occurred on an // element located within the relatedTarget element in the DOM relatedTarget: this.getRelatedTarget(event), // If the event was a keyboard-related one, key returns the character key: this.getCharacterFromKey(event), // Return the x and y coordinates of the mouse pointer, relative to the document pageX: page.x, pageY: page.y, // Return the x and y coordinates of the mouse pointer, // relative to the element the current event occurred on offsetX: offset.x, offsetY: offset.y, // The preventDefault method stops the default event of the element // we're acting upon from occurring. If we were listening for click // events on a hyperlink, for example, this method would stop the // link from being followed preventDefault: function() { if (event.preventDefault) { event.preventDefault(); // W3C method } else { event.returnValue = false; // Internet Explorer method } } }; }, // The getTarget method locates the element the event occurred on getTarget: function(event) { // Internet Explorer value is srcElement, W3C value is target var target = event.srcElement || event.target; // Fix legacy Safari bug which reports events occurring on a text // node instead of an element node if (target.nodeType == 3) { // 3 denotes a text node target = target.parentNode; // Get parent node of text node } // Return the element node the event occurred on return target; }, // The getCharacterFromKey method returns the character pressed when // keyboard events occur. You should use the keypress event // as others vary in reliability getCharacterFromKey: function(event) { var character = ""; if (event.keyCode) { // Internet Explorer character = String.fromCharCode(event.keyCode); } else if (event.which) { // W3C character = String.fromCharCode(event.which); } return character; }, // The getMousePositionRelativeToDocument method returns the current // mouse pointer position relative to the top left edge of the current page getMousePositionRelativeToDocument: function(event) { var x = 0, y = 0; if (event.pageX) { // pageX gets coordinates of pointer from left of entire document x = event.pageX; y = event.pageY; } else if (event.clientX) { // clientX gets coordinates from left of current viewable area // so we have to add the distance the page has scrolled onto this value x = event.clientX + document.body.scrollLeft + document.documentElement.scrollLeft; y = event.clientY + document.body.scrollTop + document.documentElement.scrollTop; } // Return an object literal containing the x and y mouse coordinates return { x: x, y: y } }, // The getMousePositionOffset method returns the distance of the mouse // pointer from the top left of the element the event occurred on getMousePositionOffset: function(event) { var x = 0, y = 0; if (event.layerX) { x = event.layerX; y = event.layerY; } else if (event.offsetX) { // Internet Explorer- proprietary x = event.offsetX; y = event.offsetY; } // Returns an object literal containing the x and y coordinates of the // mouse relative to the element the event fired on return { x: x, y: y } }, // The getRelatedTarget method returns the element node the event was set up to // fire on, which can be different from the element the event actually fired on getRelatedTarget: function(event) { var relatedTarget = event.relatedTarget; if (event.type == "mouseover") { // With mouseover events, relatedTarget is not set by default relatedTarget = event.fromElement; } else if (event.type == "mouseout") { // With mouseout events, relatedTarget is not set by default relatedTarget = event.toElement; } return relatedTarget; } }; 调用方式: // Clicking anywhere on the page will output the current coordinates of the mouse pointer $.Events.add(document.body, "click", function(e) { alert("Mouse clicked at 'x' position " + e.pageX + " and 'y' position "+ e.pageY); }); 4). 加入AJAX异步加载机制。我们都知道Ajax的到来给JS带来了无限的活力,它拥有很多 "了,赶快加入到我们的鲜明的特性,如无刷新,动态异步加载等。我们可不能丢掉这块"肥肉 JS基础框架体系中把。 // Define a new namespace within the $ library, called Remote, to store our Ajax methods $.prototype.Remote = { // The getConnector method returns the base object for performing // dynamic browser-server communication through JavaScript getConnector: function() { var connectionObject = null; if (window.XMLHttpRequest) { // If the W3C-supported request object is available, use that connectionObject = new XMLHttpRequest(); } else if (window.ActiveXObject) { // Otherwise, if the IE-proprietary object is available, use that connectionObject = new ActiveXObject('Microsoft.XMLHTTP'); } // Both objects contain virtually identical properties and methods // so it's just a case of returning the correct one that's supported // within the current browser return connectionObject; }, // The configureConnector method defines what should happen while the // request is taking place, and ensures that a callback method is executed // when the response is successfully received from the server configureConnector: function(connector, callback) { // The readystatechange event fires at different points in the life cycle // of the request, when loading starts, while it is continuing and again when it ends connector.onreadystatechange = function() { // If the current state of the request informs us that the current request has completed if (connector.readyState == 4) { // Ensure the HTTP status denotes successful download of content if (connector.status == 200) { // Execute the callback method, passing it an object // literal containing two properties, the raw text of the // downloaded content and the same content in XML format, // if the content requested was able to be parsed as XML. // We also set its owner to be the connector in case this // object is required in the callback function callback.call(connector, { text: connector.responseText, xml: connector.responseXML }); } } }; }, // The load method takes an object literal containing a URL to load and a method // to execute once the content has been downloaded from that URL. Since the // Ajax technique is asynchronous, the rest of the code does not wait for the // content to finish downloading before continuing, hence the need to pass in // the method to execute once the content has downloaded in the background. load: function(request) { // Take the url from the request object literal input, // or use an empty string value if it doesn't exist var url = request.url || ""; // Take the callback method from the request input object literal, // or use an empty function if it is not supplied var callback = request.callback || function() {}; // Get our cross-browser connection object var connector = this.getConnector(); if (connector) { // Configure the connector to execute the callback method once the // content has been successfully downloaded this.configureConnector(connector, callback); // Now actually make the request for the contents found at the URL connector.open("GET", url, true); connector.send(""); } }, // The save method performs an HTTP POST action, effectively sending content, // such as a form's field values, to a server-side script for processing save: function(request) { var url = request.url || ""; var callback = request.callback || function() {}; // The data variable is a string of URL-encoded name-value pairs to send to // the server in the following format: "parameter1=value1¶meter2=value2&..." var data = request.data || ""; var connector = this.getConnector(); if (connector) { this.configureConnector(connector, callback); // Now actually send the data to script found at the URL connector.open("POST", url, true); connector.setRequestHeader("Content-type", "application/x-www-form-urlencoded"); connector.setRequestHeader("Content-length", data.length); connector.setRequestHeader("Connection", "close"); connector.send(data); } } }; 调用方式: // Load the contents of the URL index.html from the root of the web server $.Remote.load({ url: "/index.html", callback: function(response) { // Get the plain text contents of the file var text = response.text; // If the HTML file was written in XHTML format, it would be available // in XML format through the response.xml property var xml = response.xml; // Output the contents of the index.html file as plain text alert(text); } }); // Send some data to a server-side script at the URL process-form.php $.Remote.save({ url: "/index.html", data: "name=Miracle&surname=He", callback: function(response) { // Output the server-side script's response to the form submission alert(response.text); } }); 5). 建立工具类库。可封装许多常用的功能,使开发者能以更快捷的方式实现功能。 // Add the Utils namespace to hold a set of useful, reusable methods $.prototype.Utils = { // The mergeObjects method copies all the property values of one object literal into another, // replacing any properties that already exist, and adding any that don't mergeObjects: function(original, newObject) { // for ... in ... loops expose unwanted properties such as prototype // and constructor, among others. Using the hasOwnProperty // native method allows us to only allow real properties to pass for (var key in newObject) { if (newObject.hasOwnProperty(key)) { // Loop through every item in the new object literal, // getting the value of that item in the original object and // the equivalent value in the original object, if it exists var newPropertyValue = newObject[key]; var originalPropertyValue = original[key]; } // Set the value in the original object to the equivalent value from the // new object, except if the property's value is an object type, in // which case call this method again recursively, in order to copy every // value within that object literal also original[key] = (originalPropertyValue && typeof newPropertyValue == 'object' && typeof originalPropertyValue == 'object') ? this.mergeObjects(originalPropertyValue, newPropertyValue) : newPropertyValue; } // Return the original object, with all properties copied over from the new object return original; }, // The replaceText method takes a text string containing placeholder values and // replaces those placeholders with actual values passed in through the values // object literal. // For example: "You have {count} messages in the {folderName} folder" // Each placeholder, marked with braces – { } – will be replaced with the // actual value from the values object literal, the properties count and // folderName will be sought in this case replaceText: function(text, values) { for (var key in values) { if (values.hasOwnProperty(key)) { // Loop through all properties in the value object literal if (typeof values[key] == undefined) { // Code defensively values[key] = ""; } // Replace the property name wrapped in braces from the text // string with the actual value of that property. The regular // expression ensures that multiple occurrences are replaced text = text.replace(new RegExp("{" + key +"}", "g"), values[key]); } } // Return the text with all placeholder values replaced with real ones return text; }, // The toCamelCase method takes a hyphenated value and converts it into // a camel case equivalent, e.g., margin-left becomes marginLeft. Hyphens // are removed, and each word after the first begins with a capital letter toCamelCase: function(hyphenatedValue) { var result = hyphenatedValue.replace(/-\D/g, function(character) { return character.charAt(1).toUpperCase(); }); return result; }, // The toHyphens method performs the opposite conversion, taking a camel // case string and converting it into a hyphenated one. // e.g., marginLeft becomes margin-left toHyphens: function(camelCaseValue) { var result = camelCaseValue.replace(/[A-Z]/g, function(character) { return ('-'+ character.charAt(0).toLowerCase()); }); return result; } }; 调用方式: // Combine two object literals var creature = { face: 1, arms: 2, legs: 2 }; var animal = { legs: 4, chicken: true }; // Resulting object literal becomes... // { // face: 1, // arms: 2, // legs: 4, // chicken: true // } creature = $.Utils.mergeObjects(creature, animal); // Outputs "You have 3 messages waiting in your inbox."; $.Utils.replaceText("You have {count} messages waiting in your {folder}.", { count: 3, folder: "inbox" }); // Outputs "fontFamily" alert($.Utils.toCamelCase("font-family")); // Outputs "font-Family" alert($.Utils.toHyphens("fontFamily")); 6). 处理元素样式CSS。我们一致提倡页面Html元素与样式分离,也就是说样式不应直接掺杂到页面中,要构建高性能且富有 关于同志近三年现实表现材料材料类招标技术评分表图表与交易pdf视力表打印pdf用图表说话 pdf 现力的页面,我们可以动态添加或移除元素样式以更好的表现。 // Define the CSS namespace within the $ library to store style-related methods $.prototype.CSS = { // The getAppliedStyle method returns the current value of a specific // CSS style property on a particular element getAppliedStyle: function(element, styleName) { var style = ""; if (window.getComputedStyle) { // W3C-specific method. Expects a style property with hyphens style = element.ownerDocument.defaultView.getComputedStyle(element, null) .getPropertyValue($.Utils.toHyphens(styleName)); } else if (element.currentStyle) { // Internet Explorer-specific method. Expects style property names in camel case style = element.currentStyle[$.Utils.toCamelCase(styleName)]; } // Return the value of the style property found return style; }, // The getArrayOfClassNames method is a utility method which returns an // array of all the CSS class names assigned to a particular element. // Multiple class names are separated by a space character getArrayOfClassNames: function(element) { var classNames = []; if (element.className) { // If the element has a CSS class specified, create an array classNames = element.className.split(' '); } return classNames; }, // The addClass method adds a new CSS class of a given name to a particular element addClass: function(element, className) { // Get a list of the current CSS class names applied to the element var classNames = this.getArrayOfClassNames(element); // Add the new class name to the list classNames.push(className); // Convert the list in space-separated string and assign to the element element.className = classNames.join(' '); }, // The removeClass method removes a given CSS class name from a given element removeClass: function(element, className) { var classNames = this.getArrayOfClassNames(element); // Create a new array for storing all the final CSS class names in var resultingClassNames = []; for (var index = 0; index < classNames.length; index++) { // Loop through every class name in the list if (className != classNames[index]) { // Add the class name to the new list if it isn't the one specified resultingClassNames.push(classNames[index]); } } // Convert the new list into a space-separated string and assign it element.className = resultingClassNames.join(" "); }, // The hasClass method returns true if a given class name exists on a // specific element, false otherwise hasClass: function(element, className) { // Assume by default that the class name is not applied to the element var isClassNamePresent = false; var classNames = this.getArrayOfClassNames(element); for (var index = 0; index < classNames.length; index++) { // Loop through each CSS class name applied to this element if (className == classNames[index]) { // If the specific class name is found, set the return value to true isClassNamePresent = true; } } // Return true or false, depending on if the specified class name was found return isClassNamePresent; }, // The getPosition method returns the x and y coordinates of the top-left // position of a page element within the current page, along with the // current width and height of that element getPosition: function(element) { var x = 0, y = 0; var elementBackup = element; if (element.offsetParent) { // The offsetLeft and offsetTop properties get the position of the // element with respect to its parent node. To get the position with // respect to the page itself, we need to go up the tree, adding the // offsets together each time until we reach the node at the top of // the document, by which point, we'll have coordinates for the // position of the element in the page do { x += element.offsetLeft; y += element.offsetTop; // Deliberately using = to force the loop to execute on the next // parent node in the page hierarchy } while (element = element.offsetParent) } // Return an object literal with the x and y coordinates of the element, // along with the actual width and height of the element return { x: x, y: y, height: elementBackup.offsetHeight, width: elementBackup.offsetWidth } } }; 调用方式: // Locate the first
element within the page var horizontalRule = document.getElementsByTagName("hr")[0]; // Output the current width of the
element alert($.CSS.getAppliedStyle(horizontalRule, "width")); // Add the hide CSS class to the
element $.CSS.addClass(horizontalRule, "hide"); // Remove the hide CSS class from the
element $.CSS.removeClass(horizontalRule, "hide"); // Outputs true if the hide CSS class exists on the
element alert($.CSS.hasClass(horizontalRule, "hide")); // Outputs the x and y coordinates of the
element var position = $.CSS.getPosition(horizontalRule); alert("The element is at 'x' position '" + position.x + "' and 'y' position '" + position.y + "'. It also has a width of '" + position.width + "' and a height of '" + position.height + "'"); 7). 快速定位DOM元素。改进JS获取元素的复杂性,提供更快捷的方式获取。 // Add a new Elements namespace to the $ library $.prototype.Elements = { // The getElementsByClassName method returns an array of DOM elements // which all have the same given CSS class name applied. To improve the speed // of the method, an optional contextElement can be supplied which restricts the // search to only those child nodes within that element in the node hierarchy getElementsByClassName: function(className, contextElement) { var allElements = null; if (contextElement) { // Get an array of all elements within the contextElement // The * wildcard value returns all tags allElements = contextElement.getElementsByTagName("*"); } else { // Get an array of all elements, if no contextElement was supplied allElements = document.getElementsByTagName("*"); } var results = []; for (var elementIndex = 0; elementIndex < allElements.length; elementIndex++) { // Loop through every element found var element = allElements[elementIndex]; // If the element has the specified class, add that element to the output array if ($.CSS.hasClass(element, className)) { results.push(element); } } // Return the list of elements that contain the specific CSS class name return results; } }; 8). 可能还有很多的功能加入,并通过版本控制的方式。这里先到此为止,我们在最后初始化$类 库。 // Instantiate the $ library as a singleton right at the end of the file, // ready to use on a page which references the $.js file $ = new $();
本文档为【Javascript富客户端RIA系列之一:搭建JS框架类库】,请使用软件OFFICE或WPS软件打开。作品中的文字与图均可以修改和编辑, 图片更改请在作品中右键图片并更换,文字修改请直接点击文字进行修改,也可以新增和删除文档中的内容。
该文档来自用户分享,如有侵权行为请发邮件ishare@vip.sina.com联系网站客服,我们会及时删除。
[版权声明] 本站所有资料为用户分享产生,若发现您的权利被侵害,请联系客服邮件isharekefu@iask.cn,我们尽快处理。
本作品所展示的图片、画像、字体、音乐的版权可能需版权方额外授权,请谨慎使用。
网站提供的党政主题相关内容(国旗、国徽、党徽..)目的在于配合国家政策宣传,仅限个人学习分享使用,禁止用于任何广告和商用目的。
下载需要: 免费 已有0 人下载
最新资料
资料动态
专题动态
is_882336
暂无简介~
格式:doc
大小:92KB
软件:Word
页数:28
分类:
上传时间:2018-03-17
浏览量:11