一个轻量级的HTML5网格

分享于 

25分钟阅读

Web开发

  繁體

请参见这里的action in

介绍

本文描述了如何从头创建一个HTML5网格。 它探索一种技术,其中网格骨架是用占位符创建的,然后根据需要填充。 写一个自己的优点是你完全控制了你的网格能够完全控制。 如果需要改变或者添加,那么这些可以在几小时内完成,而不是在几年后发现。

请仔细看一下这里的网格 源代码在 GitHub上作为BlueSkyGrid提供。

Background

建立自己控制的想法是一个激烈的争论。 特别是一个被认为是复杂控件的网格在这个类别。

尽管 true 购买了一个准备好的网格,但是它会给你一个价格的'走出盒子'。 我们发现,如果有任何你想要的东西不是由网格提供的,你就会被卡住。 是的,可以购买源代码并尝试扩展它,但通常并不那么容易。 它还需要在供应商发布新版本或者更新时进行管理。

如果我没有错,从货架买网格有好处,它可以快速运行和运行,它可能与其他组件的( 来自同一个供应商) 很好地发挥。

所以如果你愿意妥协,那么买一个网格可能是为了你。

然而,就像我将在本文中演示的,编写你自己的网格实际上并不像你可能认为的那样难。 on你可以得到你想要的东西,如果没有工作,你可以快速找到并修复it你可以在新版本发布的情况下,快速检查供应商和faq论坛。

网格特性

网格当前提供以下功能:

  • 列调整大小,
  • 列重新排序,
  • 排序
  • 传呼
  • 灵活的列- 填充网格中任何空白的单个列。 ( 调整柔性柱的大小以禁止它弯曲,只需双击列调整器来激活这里特性) !
  • 货币

它目前缺少:

  • 过滤器( 但是现在可以限制给网格的数据) !

创建你的第一个网格

你有一个要插入网格的DOM元素。 所以首先定义一组列定义来表示你想要显示的数据。 ColDefs告诉网格 what where where where where display display。 注意,如果没有找到匹配的ColDef,则可以提供更多的数据,但不会显示。 我们还定义了一些对象,以便网格能够查找任何可能需要这里属性的字段的货币符号。

在你的客户端代码中定义了代码 below,你可以从中创建和管理网格。


// one-off, define some currencies for the CurrencyManager so it can return symbols for the grid


__cm.setCurrencyInfo(new __cm.CurrencyInfo("GBP", "British Pound", "£", "GBP", ""));


__cm.setCurrencyInfo(new __cm.CurrencyInfo("USD", "US Dollar", "$", "USD", ""));


__cm.setCurrencyInfo(new __cm.CurrencyInfo("EUR", "Euro", "€", "EUR", ""));



// define the column definitions for the grid


this._coldefs = [];


this._coldefs.push(new __gc.ColDefinition("code", "Code", 100, "", "", "", "asc"));


this._coldefs.push(new __gc.ColDefinition("fullname", "Fullname", -1)); 


this._coldefs.push(new __gc.ColDefinition("county", "County", 110, "", "", "", "", "", true));


this._coldefs.push(new __gc.ColDefinition("currency", "Currency", 90, "", "", "center"));


this._coldefs.push(new __gc.ColDefinition("valuation", "Valuation", 110, "number", "0,0.00", "right", "", "currency"));


this._coldefs.push(new __gc.ColDefinition("price", "Price", 110, "number", "0,0.00", "right", "", "currency"));


this._coldefs.push(new __gc.ColDefinition("myimage", "Img", 50, "image", "", "center"));


this._coldefs.push(new __gc.ColDefinition("created", "Created", 150, "date", "", "center"));



让我们仔细看一下'价格'行。 此行为给定数据行中的名为'价格'的属性定义了一个 ColDefinition。 如果指定'价格'。('。')。。 如果字段是'日期'类型的,它会调用 moment.js 以获取它的格式。 列也应该右对齐,并查看'货币'数据字段的货币符号。 提供货币列查找将强制网格查找等效的签名并显示。

现在我们有一组 ColDefinitions,我们找到DOM元素来放置网格,创建 GridController,并要求创建网格骨架以( 如下面的代码Fragment所示) 创建。

现在网格已经完全创建并准备接受数据了,我们创建了一些示例数据并将它的交给网格实例( 与coldefs一起使用)。

注意,在这个例子中,我们在创建网格后将 ColDefs array 与数据本身一起提供。 这显示你可以随时发送不同的数据,只要它附带一组匹配的ColDefs !


// find the element we will place the grid in


var $grid = $('.mygrid');



// create a grid controller


this._gc = new __gc.GridController();


this._gc.createGrid($grid);



// create some sample data


var data = __data.generateSampleData(rowcount);



// simply pass on the data (and their definitions) to the grid


this._gc.setData(data, this._coldefs).done(function () {


. . .


});



...



这就是将网格放置在占位符元素中的所有方法。

注意,setData调用返回一个 jQuery promise,当网格的创建完成时通知调用方。

代码

代码太多,无法详细描述网格代码,因此本文将讨论核心原则和一些关键代码部分,这些代码部分是关键的部分。 我建议复制zip文件或者在 github上查看这里代码以获得更深入的理解。

注意,我来自一个 C# background,因这里已经接受了像长丢失的子。 它允许我保持面向对象的思维模式,轻松从其他类或者实现接口派生,拥有类型安全和大量其他优秀的东西。

如上所述,可以在 找到完整的源代码,而且在我的far博客站点中可以看到它的所有荣耀的一个工作示例。 如果有足够的兴趣,我会随时编辑和增强这个网格。

网格构造

在这个网格的实例中创建一个实例的核心思想是在代码中构建,并使用'creategridstructure'函数在目标分区中注入。 这个函数在代码中构建一个字符串,它被转换成一个DOM元素( 使用 jQuery ),然后注入到等待目标元素中。

然后获取 $header 和 $datarows 节的句柄,然后填充这些节。 再次,先在代码中注入它们的占位符(。createHeader和 createRows )。

网格基于三条信息,即实际数据,列定义 array 列定义 describing描述显示该数据和目标DOM元素的列,我们将把完成的网格注入到 that。

列定义描述了从datarow中使用的数据字段。header。格式。对齐。类型(。字符串,数字,日期,图像等) 等等。

用于生成这里网格的核心类的概述显示为 below:

DataEngine.ts

为了分离控制网格操作和数据操作的关注点,创建了DataEngine的一个实例。 DataEngine的作用是保持原始数据,并管理控制器在生成行时调用的一组perpared数据。 以后可以扩展,以包括筛选。分组和/或者编辑数据的管道。

GridController.ts

GridController.ts 专注于创建从scratch中注入到给定DOM元素中的所有 HTML。 这意味着所有这些行都在排序和列重排序时重新创建。 在列调整所有影响的单元格在代码中调整,因此更快。 创建所有行并不是我发现的世界的末尾( 1000行仍然是零秒)

CurrencyMananger.ts

ColDefinition标识货币查找字段时会显示 CurrencyManager ( 像'$') field值,并返回它的符号('$'),然后将它的用作单元格格式的一部分。

在同一行中的所有网格单元都使用 css flex框放置在另一个行。 在许多 places flexboxes calling我在许多地方使用了一些有用的css类,我在其中使用了多个 div,其中一个需要div来填充任何可用空间。

检查示例 below - 有一个父 div ( 高度可能不同),它的中包含两个固定高度,一个在最底部,另一个要填充。 只要将父类设置为'flex-parent-col',就可以将它的中的一个子类设置为'柔体子',这全部都是。 行的等效行有一个'flex-parent-row'。

网格行也是基于这个结构构建的。 行本身用'flex-parent-row'类装饰,它的一个子类将它的类设置为'柔体子'以填充可用空间。

GridController.ts

以下所有代码示例都保存在的GridController.ts

below 是显示网格的核心结构创建的代码。 它生成一个字符串,然后转换为DOM元素并注入到相关的占位符中。


private createGridStructure(showBorder: boolean): string {


 var s: string = "";



 // --------------------


 // define the grid structure


 // --------------------


 var s: string = "";



 s += "<div class='bs-grid flex-parent-col" + (showBorder? "border" : "") + "'>";



 s += " <div class='spinner' style='display: none'}> <div> <i class='fa fa-spinner fa-spin fa-3x'></i> </div> </div>"



 s += " <div class='header-containment'> </div>"



 s += " <div class='header'>"


 s += " <div class='resize-marker' title='resize this column or double-click to make it the flex-column'> </div>"


 s += " <div class='insert-marker'>";


 s += " <i class='down fa fa-caret-down fa-2x'></i>";


 s += " </div>";


 s += " <div class='header-template' style='position: relative'> </div>"


 s += " </div>";



 s += " <div class='data-scrollable flex-child flex-scrollable'>";


 s += " <div class='data'> </div>";


 s += " <div class='full-resize-marker'> </div>"


 s += " </div>"



 s += "" + this.createPager();



 s += "</div>";



 return s;


}



private createPager(): string {


 var s: string = "";



 s += " <div class='pager'>"



 s += " <button type='button' class='btnFirstPage pager-button'> <i class='fa fa-step-backward' title='First page'></i> </button>";


 s += " <button type='button' class='btnPrevPage pager-button'> <i class='fa fa-caret-left fa-lg' title='Previous page'></i> </button>";


 s += " <span class='pager-text' style='margin: 0px 4px 0px 4px'></span>";


 s += " <button type='button' class='btnNextPage pager-button'> <i class='fa fa-caret-right fa-lg' title='Next page'></i> </button>";


 s += " <button type='button' class='btnLastPage pager-button'> <i class='fa fa-step-forward' title='Last page'></i> </button>";



 s += " <span style='margin: 0px 2px 0px 20px'>page size: </span>";


 s += " <select class=page-size title='Number of rows per page'></select>";



 return s;


}



并被调用为:


...



// create a grid string


var grid = this.createGridStructure(showBorder);



// create a DOM grid 


this.$grid = $(grid);



// add this grid to the given dom element


this.$grid.appendTo($el);



// get a handle to some important data parts


this.$header = $('.header-template', this.$grid);


this.$datarows = $('.data', this.$grid);


this.$pager = $('.pager', this.$grid);


this.$resizemarker = $('.resize-marker', this.$grid);


this.$resizeline = $('.full-resize-marker', this.$grid);


this.$insertmarker = $('.insert-marker', this.$grid);


this.$sgloading = $('.spinner', this.$grid);


this.$datascrollable = $('.data-scrollable', this.$grid);



...



所以首先我们调用'creategridstructure',它将整个网格作为字符串返回。 然后将这个字符串转换成一个jQuery元素并将它的附加到给定的DOM元素中。

我们的网格有结构并且被放置在客户机DOM树中。

然后我们就得到一些重要元素,比如 header。数据区域。resizemarker以及我们希望控制的其他元素。 请记住,现在我们仍然只有骨架支付,所以现在让我们创建 header 和行部分。

创建 header 和datarow的过程与网格骨架的Pattern 相同。 首先我们构建表示完整 header ( 或者数据行)的字符串,然后创建真正的DOM元素,然后将它们插入到占位符(。$header或者 $datarows ) 中。

让我们看一下构造这个 header的代码:


private createHeader(): string {



 var self = this;


 var headerTemplate: string = "<div class='row-header flex-parent-row'>";



 $.each(this.colDefinitions, function (index, coldef: ColDefinition) {



 // define the css classes we apply to each header column


 var cssclasses: string = "cell-header cell-right-column";


 cssclasses += coldef.classAlign + "";


 if (coldef.isFlexCol) cssclasses += "stretchable";



 // get and lowercase the colName allowing us to get hold of the appropriate column when taking actions like sorting, resizing etc.


 var colName = coldef.colName.toLowerCase();


 var hitem: string = "";


 hitem += "<div class='" + cssclasses + "' data-sgcol='" + colName + "' style='width:" + coldef.width + "px;'>";


 hitem += "<span>" + coldef.colHeader + "</span>";


 hitem += "<span class='" + self.getSortSymbol(coldef) + "'></span>";


 if (coldef.isFlexCol) 


 hitem += "<i class='pull-right fa fa-arrows-h' style='background-color: transparent; color: rgb(201, 201, 208);' title='this column is flexible sized'></i>";


 hitem += "</div>";


 headerTemplate += hitem;



 });


 headerTemplate += "</div>";


 return headerTemplate;


}



然后调用这里方法,如下所示:


// create the header (string and DOM and append to placeholder)


var header = self.createHeader();



$(header).appendTo(self.$header);



我们有一个 header !

然后应用于实际数据行:


private createRows(): string {


 var self = this;


 var s = "";



 for (var i = 0; i <self.dataEngine.baseDataPrepared.length; i++) {


 var dataItem: any = self.dataEngine.baseDataPrepared[i];



 // start row definition


 s += " <div class='row flex-parent-row' data-pkvalue='" + dataItem.pkvalue + "'>";



 // step through each ColDefinition


 self.colDefinitions.forEach(function (coldef: ColDefinition) {



 // retrieve the actual raw cell value


 var myvalue = dataItem[coldef.colName];



 // check if any custom formatting is required


 myvalue = self.getFormattedValue(myvalue, dataItem, coldef);



 // ask the client to fill in any generic styling properties


 var styleProp: CellStyleProperies = self.cbStyling(coldef, dataItem);



 // start Cell definition


 s += "<div class='cell cell-right-column" + (coldef.isFlexCol? "stretchable" : "") + "" + coldef.classAlign + "'";


 s += "data-sgcol='" + coldef.colName + "' style='width:" + coldef.width + "px;";



 // if a cell backcolour was given then apply it


 if (styleProp.cellBackColour) 


 s += " background-color:" + styleProp.cellBackColour + ";";



 // if a cell forecolour was given then apply it


 if (styleProp.cellForeColour) 


 s += " color:" + styleProp.cellForeColour + ";";



 s += "'>"; 



 // check if this cell is merged with an image and we were given an image


 if (coldef.mergeWithImage && styleProp.imgName) 


 s += "<div class='merged-image" + coldef.colAlign + "' style='background-color:" + styleProp.imgBackColour + "; color:" + styleProp.imgForeColour + ";'> <i class='fa" + styleProp.imgName + "'> </i> </div>"



 // if this is an image colum then just show the image


 if (coldef.colType == "image") {


 var faImage = styleProp.imgName; 


 if (faImage.length == 0) faImage = myvalue; // if no return value was given then use the actual data


 s += "<i class='fa" + faImage + "'"></i>";


 }


 else


 // simply write out the actual cell value


 s += myvalue;



 s += " </div>" // end cell definition



 });


 s += " </div>"; // end row definition


 }



 return s;


}



又一次被称为:


// create all rows (string and DOM and append to placeholder)



var rows = self.createRows();



$(rows).appendTo(self.$datarows);



这里需要注意的事项。 首先,我们迭代实际的( 已经准备) dataitems,针对每个dataitem迭代 ColDefinitions array,然后给我们提供足够的信息来定义每个单独的单元。

定义单元时,我们首先通过返回格式化值的格式化程序来推入原始值。 然后,我们通过指定适当的类并设置宽度来装饰单元格。

单元格样式

为了允许客户端定义每个单元的定制样式,我们检查是否给定了定制样式函数。 如果是这样,我们将调用当前的ColDef和实际的datarow,使客户机能够决定所有可以用的数据。

这个想法是对于每个单元,客户端创建一个CellStyleProperties对象,在由GridController定义单元时使用。 可以设置的属性包括背色和前景色。图像和其他。

要了解每个单元的自定义样式如何工作,让我们看看下面的代码( 这是在客户端代码类中定义的):


this._gc.cbStyling = function (coldef: __gc.ColDefinition, item: any) {



 // define a new CellStyleProperties object we will return


 var styleProp: __gc.CellStyleProperies = new __gc.CellStyleProperies();



 // now check which column is being checked and set any style properties appropriately 


 if (coldef.colName == "county") {


 if (item["county"] == "Kent" && Math.floor(item["price"]) % 2 == 0)


 styleProp.cellBackColour = "rgb(178, 232, 178)";


 if (Math.floor(item["price"]) % 4 == 0) 


 styleProp.imgBackColour = "rgb(255, 196, 8)";


 if (item["county"] == "Sussex")


 styleProp.imgForeColour = "red";


 }



 // image


 if (coldef.colName == "myimage") {


 if (item["price"] <250)


 styleProp.imgName = "fa-floppy-o";


 else if (item["price"] <500)


 styleProp.imgName = "fa-warning";


 }



. . .


. . .



 return styleProp;


};



这个函数为每个单元格调用,因为它们在构造时允许我们高度定制。

网格图像

你可能已经注意到我的首选方法是使用字体awesome图片。 由于这些工作通过使用特定类装饰元素,它们是在代码中构建网格的理想。

关于事件处理的一个词

创建网格骨架并获得它的占位符后,我们将处理程序附加到其中一些。 这些包括导航按钮的单击处理程序,行单击和双击,还有。

上面 提到的事件处理程序作为'一个关闭'连接,如果没有重新创建的话。 网格标头是不同的,因为它被频繁地重新创建。 因这里,附加可以拖动和 droppable jquery ui交互的代码允许我们将列移动到一个单独的函数中,每次创建 header。

选择行

为了支持选择行的能力,DataEngine确保一个名为'pkvalue'的属性被附加到每个生成的行。 使用名为'数据 pkvalue'的数据属性将这个唯一值放在每个DOM行中,这样当单击了jQuery以便将'选定'类添加到行元素。

Ajax

在本文中,网格接收到由DataEngine从开始控制和控制的所有数据。 尽管javascript似乎能够很好地保存和处理大量数据,但是并没有停止扩展DataEngine并调用服务器。 只要确保在JQueryPromise调用中使用 <> 作为返回对象,并相应地调整代码。

结束语

本文希望演示创建你自己的网格是一个能够解决的问题。 在当前状态下,添加更多的功能是不困难的。 如果有足够的兴趣,我将增强网格来公开诸如过滤。公式列和ajax调用等功能。

对于我的雇主,我创建了一个类似的网格,它具有许多功能,使用击倒绑定,例如填充单元格内容,宽度,顺序,动态更新数据和更多的数据。 在这里我试图保持清除绑定,使事情简单,并通过代码完成所有事情。

请查看 GitHub存储库,其中包含源代码,并且可以随意插入代码和/或者对项目做出贡献

Points of interest

首先,我很怀疑extend浏览器和 JavaScript to回调callback多次回调 callback fly DOM fly JS JS JS JS compilers compilers compilers compilers maniupulations ! 我们必须为JavaScript编译器和optimisers提供更多的信用 !

历史记录

vs 1.0 - 初始版本。

vs 1.1 - 更新演示网格的域引用


Light  Html5  GRID  scratch  
相关文章