Source: jquery.scrollableTools.js

  1. "use strict";
  2. /**
  3. *jquery.scrollableTools.js
  4. *任意のテーブルをスクロール可能とするjQueryプラグイン.
  5. *指定の幅・高さに領域を固定し,ヘッダーとフッター及び列の表示位置を固定することで大きなtableの可視性を高めます.
  6. *従来のプラグインと比較し,既存のレイアウトを極力汚染しないように気をつけた.
  7. *target browser:firefox,chrome,opera,safari,ie6,ie7,ie8,ie9
  8. *※ブラウザ間の完全な互換を謳うわけではありません.
  9. *
  10. *[使い方]
  11. *1.tableタグに引数を設定
  12. *<table id="b" cellspacing="0" scrollable="true" fixwidth="300" fixheight="300" flozenf="1" flozenl="1">
  13. *2.コード内部で次を指定
  14. *$("#tblA").scrollable(tableWidth, tableHeight, flozenF, flozenL);
  15. *
  16. *[history]
  17. *2012/02/04 初版・新規作成.
  18. *2012/02/04 列の追加を行った際の再設定を可能とした.
  19. *2012/02/04 border-collapse:collapseが設定されていた際のcellSpacing値の取得を修正.
  20. *2012/02/04 ie6-8では動作しないように処理を追加.
  21. *2012/02/05 ie8で動作するように機能を追加.
  22. *2012/02/05 ie6,7で動作するように機能を追加.
  23. *2012/02/06 ie9で動作するように変更.(一部不具合有り)
  24. *2012/02/06 header,footerの行毎の高さ設定に対応.
  25. *2012/02/06 IE8において固定列を変更した際に不要なdiv要素が残ってしまうのを修正.
  26. *2012/02/06 縦スクロールと比較し,横スクロールは処理のコストが高いため,列の固定処理はsetIntervalで行う.
  27. *2012/02/06 cssセレクタ記述の厳密化とセレクタの指定ミスを修正.
  28. *2012/02/06 スクロール処理の若干の軽量化.
  29. *2012/02/06 コードの整理.IE6,7で動作しなくなっていたのを修正.
  30. *2012/02/07 全体的な軽量化を図った.
  31. *2012/02/08 横スクロール時の軽量化を行った.
  32. *2012/02/08 スクロール指定時にscrolled=trueを設定するようにした.
  33. *2012/02/10 tableタグへのマークアップのみで動作するように変更.scrollable="true"で動作する.
  34. *2012/02/10 列の固定を指定しなかった場合にエラーとなる部分を修正.
  35. *NOTE:pushpin headerと異なる点はスクロールバーの高さがテーブル全体の高さとなること.
  36. *NOTE:タイマーは必要?→行数が増えると余りに負荷が高い.
  37. *@author DEFGHI1977@xboxlive http://defghi1977-onblog.blogspot.com/
  38. *
  39. *[使用許諾/免責]
  40. *・本プログラムの著作権はDEFGHI1977@xboxliveに帰属する.
  41. *・本コードを利用することで発生した不具合・損害については当方は一切関知しないこととする
  42. *上記に承諾した場合に限り商用・私用を問わず,用途を問わず利用可能とする.
  43. *コードの改変・修正については自己責任で行う限り禁止しない.
  44. */
  45. (function(){
  46. //from http://d.hatena.ne.jp/amachang/20071010/1192012056
  47. /*@cc_on
  48. var doc = document;
  49. eval('var document = doc');
  50. @*/
  51. /**
  52. *列固定処理のインターバル
  53. */
  54. var synchronizeInterval = 20;
  55. /**
  56. *列固定処理のインターバルの属性名
  57. */
  58. var TIMER_ID = "timerId";
  59. /**
  60. *空文字列
  61. */
  62. var NULL_STR = "";
  63. /**
  64. *jQuery拡張:テーブルにスクロール機能を追加する.
  65. *@param tableWidth テーブルの表示幅
  66. *@param tableHeight テーブルの表示高
  67. *@param flozenF 固定列数(左)
  68. *@param flozenL 固定列数(右)
  69. *@return 処理対象となったjQueryオブジェクト
  70. */
  71. jQuery.fn.scrollable = function(
  72. tableWidth, tableHeight, flozenF, flozenL){
  73. this.each(function(){
  74. setScrollable(jQuery(this), tableWidth, tableHeight, flozenF, flozenL);
  75. });
  76. return this;
  77. };
  78. /**
  79. *テーブルにスクロール機能を追加する.
  80. *table要素でなかったら何もしない.
  81. *無引数の場合,属性値が設定されていたらそちらを参照する.
  82. *@param singleTable 単一テーブルのjqueryオブジェクト
  83. *@param tableWidth テーブルの表示幅
  84. *@param tableHeight テーブルの表示高
  85. *@param flozenF 固定列数(左)
  86. *@param flozenL 固定列数(右)
  87. *@return 処理対象となったjQueryオブジェクト
  88. */
  89. function setScrollable(singleTable ,tableWidth, tableHeight, flozenF, flozenL){
  90. if(!singleTable.is("table")){
  91. return singleTable;
  92. }
  93. //引数調整
  94. if(!tableWidth){
  95. if(singleTable.attr("fixWidth")){
  96. tableWidth = ~~singleTable.attr("fixWidth");
  97. }else{
  98. tableWidth = 300;
  99. }
  100. }
  101. if(!tableHeight){
  102. if(singleTable.attr("fixHeight")){
  103. tableHeight = ~~singleTable.attr("fixHeight");
  104. }else{
  105. tableHeight = 300;
  106. }
  107. }
  108. if(!flozenF){
  109. if(singleTable.attr("flozenF")){
  110. flozenF = ~~singleTable.attr("flozenF");
  111. }else{
  112. flozenF = 0;
  113. }
  114. }
  115. if(!flozenL){
  116. if(singleTable.attr("flozenL")){
  117. flozenL = ~~singleTable.attr("flozenL");
  118. }else{
  119. flozenL = 0;
  120. }
  121. }
  122. singleTable.attr("fixWidth", tableWidth)
  123. .attr("fixHeight", tableHeight)
  124. .attr("flozenF", flozenF)
  125. .attr("flozenL", flozenL)
  126. .attr("scrollable", true);
  127. //IE判定
  128. var ie = isIE();
  129. var func;
  130. if(!ie){
  131. func = forOther.addScrollFunction;
  132. }else if(ie == 8){
  133. func = forIE8.addScrollFunction;
  134. }else{
  135. func = forIE.addScrollFunction;
  136. }
  137. func(singleTable, tableWidth, tableHeight, flozenF, flozenL);
  138. return singleTable;
  139. }
  140. /**
  141. *実行環境がIEかどうかを判定する
  142. *NOTE:document.ready後でないと正しく動作しない.
  143. *@return IEのバージョン番号(他のブラウザではundefined)
  144. */
  145. var ieVal = 0;
  146. function isIE(){
  147. if(ieVal == 0){
  148. if(document.body.style.maxHeight != undefined){
  149. ieVal = (!/*@cc_on!@*/false) ? undefined: !document.documentMode ? 7: document.documentMode;
  150. return ieVal;
  151. } else {
  152. ieVal = 6;
  153. return ieVal;// IE6, older browsers
  154. }
  155. }else{
  156. return ieVal;
  157. }
  158. }
  159. /**
  160. *スクロールバーの幅を取得する.
  161. *NOTE:document.ready後でないと正しく動作しない.
  162. *@return スクロールバーの幅
  163. */
  164. function getScrollbarWidth(){
  165. var body = jQuery("body");
  166. var outer = jQuery("<div>");
  167. outer.css({
  168. width: 100,
  169. height: 100,
  170. overflow: "scroll",
  171. border: "none",
  172. padding: 0,
  173. margin: 0,
  174. visibility: "hidden"
  175. });
  176. var inner = jQuery("<div>&nbsp;</div>");
  177. inner.css({
  178. width: 200,
  179. height: 200,
  180. border: "none",
  181. padding: 0,
  182. margin: 0
  183. });
  184. outer.append(inner).appendTo(body);
  185. outer.scrollTop(200);
  186. var barWidth = outer.scrollTop() - 100;
  187. outer.remove();
  188. return barWidth;
  189. }
  190. /**
  191. *テーブルのIDを生成する.
  192. *@return テーブルのID
  193. */
  194. var cnt = 0;
  195. var SCROLLABLE = "scrollable_";
  196. function getId(){
  197. cnt++;
  198. return "scrollable_" + cnt;
  199. }
  200. /**
  201. *セルの相対位置を削除する.
  202. *この値を削除することでスタイルシートの位置に初期化出来る.
  203. *@param jqTbl 削除対象のテーブル
  204. */
  205. function resetPosition(jqTbl){
  206. jqTbl.find("td,th").css({
  207. top: NULL_STR, left: NULL_STR
  208. });
  209. }
  210. /**
  211. *スタイル文字列の生成ヘルパーオブジェクト
  212. *ie6の場合はセレクタ「>」を指定しても無視する.
  213. *@param top トップレベルエレメントのID
  214. *@param selectors セレクタ文字列の配列
  215. *@param css スタイル値のhash
  216. */
  217. function styleBuilder(top, selectors, css){
  218. this.top = top;
  219. this.selectors = selectors;
  220. this.css = css;
  221. }
  222. (function(p){
  223. /**
  224. *トップレベルエレメントのID
  225. */
  226. p.top = NULL_STR;
  227. /**
  228. *セレクタ文字列を格納するフィールド
  229. */
  230. p.selectors = new Array();
  231. /**
  232. *cssスタイルシート値を格納するフィールド
  233. */
  234. p.css = new Object();
  235. /**
  236. *スタイル文字列を取得する
  237. *@return スタイル文字列
  238. */
  239. p.toString = function(){
  240. var len = this.selectors.length;
  241. if(this.top == NULL_STR || len == 0){
  242. //throw new TypeError("missing parameters.");
  243. return NULL_STR;
  244. }
  245. var i;
  246. for(i = 0; i<len; i++){
  247. if(this.selectors[i].match(",")){
  248. throw new TypeError("some selectors contain [,].");
  249. }
  250. }
  251. var top = "#" + this.top;
  252. var str = top + " " + this.selectors.join("," + top + " ");
  253. str += "{\r";
  254. for(i in this.css){
  255. str += " " + i + ":" + this.css[i] + ";\r";
  256. }
  257. str += "}";
  258. //ie6,7の場合はセレクタ「>」が利用不可
  259. if(isIE() == 6 || isIE() == 7){
  260. str = str.replace(/>/g, " ");
  261. }
  262. return str;
  263. };
  264. })(styleBuilder.prototype);
  265. /**
  266. *pxを取り除いた値を取得する.
  267. *@param 値取得対象の文字列
  268. *@return 変換後の値
  269. */
  270. var PX = "px";
  271. function removePx(str){
  272. return ~~(str.replace(PX, NULL_STR));
  273. }
  274. /**
  275. *カラムにクラスを設定する.
  276. *左から[f0,f1…l1,l0]
  277. *@param tbl 機能を追加する対象のtblエレメント
  278. *@param flozenF 固定列(左)
  279. *@param flozenL 固定列(右)
  280. */
  281. function appendRowClass(
  282. jqTbl, flozenF, flozenL){
  283. var tblId = jqTbl.attr("id");
  284. var rows = jqTbl.find("tr");
  285. var colCount = rows[0].children.length;
  286. var i, j, cells;
  287. for(i = 0; i<flozenF; i++){
  288. cells = rows.find("td:eq(" + i + "),th:eq(" + i + ")");
  289. cells.addClass("f" + i);
  290. }
  291. for(i = 0; i<flozenL; i++){
  292. j = colCount - i - 1;
  293. cells = rows.find("td:eq(" + j + "),th:eq(" + j + ")");
  294. cells.addClass("l" + i);
  295. }
  296. }
  297. /**
  298. *スタイル要素を削除する.
  299. *@param jqTbl スタイルを削除するテーブル
  300. */
  301. function removeStyleElem(jqTbl){
  302. jQuery("#" + getStyleId(jqTbl)).remove();
  303. }
  304. /**
  305. *スタイル要素を追加する.
  306. *@param jqTbl スタイルを追加するテーブル
  307. *@param style スタイル文字列
  308. */
  309. function appendStyleElem(jqTbl, style){
  310. jQuery("head").append(
  311. '<style id="' + getStyleId(jqTbl) + '" type="text/css">' + style + '</style>');
  312. }
  313. /**
  314. *テーブルをdivタグで2重に囲む.
  315. *@param jqTbl 対象のTBL
  316. */
  317. function wrapTableByDivs(jqTbl){
  318. var tblId = jqTbl.attr("id");
  319. jqTbl.wrap(
  320. '<div id="' + getScrollerId(jqTbl) + '"><div id="' + getFixerId(jqTbl) + '"></div></div>'
  321. );
  322. }
  323. /**
  324. *テーブルの殻を取得する.(スクロール部)
  325. *@param jqTbl 対象のTBL
  326. *@return スクロール部におけるjQueryオブジェクト
  327. */
  328. function getScroller(jqTbl){
  329. return jQuery("#" + getScrollerId(jqTbl));
  330. }
  331. /**
  332. *テーブルの殻を取得する.(サイズの固定部)
  333. *@param jqTbl 対象のTBL
  334. */
  335. function getFixer(jqTbl){
  336. return jQuery("#" + getFixerId(jqTbl));
  337. }
  338. /**
  339. *テーブルのIDを取得する.
  340. *@param jqTbl 対象のTBL
  341. */
  342. function getId(jqTbl){
  343. var tblId = jqTbl.attr("id");
  344. if(tblId == NULL_STR){
  345. tblId = getId();
  346. jqTbl.attr("id", tblId);
  347. }
  348. return tblId;
  349. }
  350. /**
  351. *テーブルのスタイル要素のIDを取得する.
  352. *@param jqTbl 対象のTBL
  353. */
  354. var _STYLE = "_style";
  355. function getStyleId(jqTbl){
  356. return getId(jqTbl) + _STYLE;
  357. }
  358. /**
  359. *テーブルの殻のIDを取得する.(スクロール部)
  360. *@param jqTbl 対象のTBL
  361. */
  362. var _SCROLLER = "_scroller";
  363. function getScrollerId(jqTbl){
  364. return getId(jqTbl) + _SCROLLER;
  365. }
  366. /**
  367. *テーブルの殻のIDを取得する.(サイズの固定部)
  368. *@param jqTbl 対象のTBL
  369. */
  370. var _FIXER = "_fixer";
  371. function getFixerId(jqTbl){
  372. return getId(jqTbl) + _FIXER;
  373. }
  374. /**
  375. *テーブル内部のDIVのクラス名を取得する.
  376. *@param jqTbl 対象のTBL
  377. */
  378. var _DIV = "_div";
  379. function getDivClass(jqTbl){
  380. return getId(jqTbl) + _DIV;
  381. }
  382. /**
  383. *内部要素のスタイルを配列として取得する.
  384. *@param jq 入手対象のjQueryオブジェクト
  385. *@return スタイルの配列
  386. */
  387. function toStyleArr(jq){
  388. var styles = new Array();
  389. var elems = jq.get();
  390. for(var i = 0, len = elems.length; i<len; i++){
  391. styles.push(elems[i].style);
  392. }
  393. return styles;
  394. }
  395. /**
  396. *レガシーIE以外のブラウザ用
  397. */
  398. var forOther = new (function(){
  399. /**
  400. *Tableにスクロール機能を追加する.
  401. *@param tbl 機能を追加する対象のtblエレメント
  402. *@param tableWidth テーブルの幅
  403. *@param tableHeight テーブルの高さ
  404. *@param flozenF 固定列(左)
  405. *@param flozenL 固定列(右)
  406. */
  407. this.addScrollFunction = function(
  408. tbl, tableWidth, tableHeight, flozenF, flozenL){
  409. var jqTbl = jQuery(tbl);
  410. jqTbl.css("visibility","hidden");
  411. //インターバルをクリア
  412. clearInterval(~~jqTbl.attr(TIMER_ID));
  413. //スタイルシートを追加
  414. appendStyle(
  415. jqTbl, tableWidth, tableHeight, flozenF, flozenL);
  416. //タグの順番を変更
  417. switchOrder(jqTbl);
  418. //セルのポジションのリセット
  419. resetPosition(jqTbl);
  420. //イベントの追加
  421. addScrollEvent(jqTbl, flozenF, flozenL);
  422. jqTbl.css("visibility", NULL_STR);
  423. };
  424. /**
  425. *スクロールイベントの処理を追加する
  426. *@param jqTbl イベントを追加したい対象のテーブル
  427. *@param flozenF 固定したい左列数
  428. *@param flozenL 固定したい右列数
  429. */
  430. function addScrollEvent(jqTbl, flozenF, flozenL){
  431. jqTbl.unbind("scroll");
  432. jqTbl.bind(
  433. "scroll",
  434. getScrollAction(jqTbl, flozenF, flozenL));
  435. }
  436. /**
  437. *スクロール処理のactionを取得する
  438. *@param jqTbl 対象テーブル
  439. *@param flozenF 固定したい左列数
  440. *@param flozenL 固定したい右列数
  441. *@return スクロール処理の関数
  442. */
  443. function getScrollAction(jqTbl, flozenF, flozenL){
  444. var PX = "px", AUTO = "auto";
  445. //スクロールバーの幅
  446. var barWidth = getScrollbarWidth();
  447. //要素群
  448. var header = jqTbl.find("thead");
  449. var body = jqTbl.find("tbody");
  450. var footer = jqTbl.find("tfoot");
  451. var footerOuterHeight = footer.outerHeight();
  452. var rows = jqTbl.find("tr");
  453. //要素の大きさ
  454. var tableWidth = removePx(jqTbl.css("width"));
  455. var bodyWidth = body.innerWidth();
  456. var tableHeight = removePx(jqTbl.css("height"));
  457. var bodyHeight = body.outerHeight();
  458. //右要素位置の差分
  459. var leftDiff = tableHeight > bodyHeight
  460. ? 0
  461. : barWidth;
  462. //下要素位置の差分
  463. var topDiff = tableWidth > bodyWidth
  464. ? tableHeight - footerOuterHeight
  465. : tableHeight - footerOuterHeight - barWidth;
  466. //styles
  467. var headerElemStyle,footerElemStyle;
  468. if(header.length > 0){
  469. headerElemStyle = header[0].style;
  470. }
  471. if(footer.length > 0){
  472. footerElemStyle = footer[0].style;
  473. }
  474. var i, col;
  475. //左の固定セル
  476. var lefts = new Array();
  477. var leftPos = new Array();//表示の基準位置
  478. for(i = 0; i < flozenF; i++){
  479. col = rows.find("th:eq(" + i + "),td:eq(" + i + ")");
  480. lefts.push(toStyleArr(col));
  481. leftPos.push(removePx(jQuery(col[0]).css("left")));
  482. }
  483. //右の固定セル
  484. var rights = new Array();
  485. var r_leftPos = new Array(); //表示の基準位置
  486. var colCount = jqTbl.find("tr:eq(0)").children().length;
  487. for(i = 0; i < flozenL; i++){
  488. var j = colCount - i - 1;
  489. col = rows.find("th:eq(" + j + "),td:eq(" + j + ")");
  490. rights.push(toStyleArr(col));
  491. var cellLeft = removePx(jQuery(col[0]).css("left"));
  492. //tableの右端にセルが並ぶようにleftをずらしておく.
  493. r_leftPos.push(cellLeft - bodyWidth + tableWidth);
  494. }
  495. //位置の同期を取るための位置情報
  496. //次回の位置
  497. var nextLeft = 0;
  498. var mooving = false;
  499. //前回の位置
  500. var preTop;
  501. var preLeft;
  502. fixRows(0);
  503. fixColumns(0);
  504. //列固定はインターバル処理とする
  505. var timerId = setInterval(function(){
  506. if(!mooving && preLeft != nextLeft){
  507. preLeft = nextLeft;
  508. mooving = true;
  509. fixColumns(nextLeft);
  510. mooving = false;
  511. }
  512. }, synchronizeInterval);
  513. jqTbl.attr(TIMER_ID, timerId);
  514. /**
  515. *行を固定する
  516. *@param top 縦方向のスクロール量
  517. */
  518. function fixRows(top){
  519. //面倒だが処理を少しでも軽くするため,生のElementを操作する.
  520. if(headerElemStyle){
  521. headerElemStyle.top = top + PX;
  522. }
  523. var f_top = top + topDiff;
  524. if(footerElemStyle){
  525. var tmp = footerElemStyle;
  526. tmp.top = f_top + PX;
  527. tmp.bottom = AUTO;
  528. }
  529. }
  530. /**
  531. *列を固定する
  532. *@param left 横方向のスクロール量
  533. */
  534. function fixColumns(left){
  535. var r_left = left - leftDiff;
  536. //面倒だが処理を少しでも軽くするため,生のElementを操作する.
  537. var i, len, col, j, len2, leftPosText;
  538. for(i = 0,len = lefts.length; i<len; i++){
  539. col = lefts[i];
  540. leftPosText = (left + leftPos[i]) + PX;
  541. for(j = 0, len2 = col.length; j<len2; j++){
  542. col[j].left = leftPosText;
  543. }
  544. }
  545. for(i = 0,len = rights.length; i<len; i++){
  546. col = rights[i];
  547. leftPosText = (r_left + r_leftPos[i]) + PX;
  548. for(j = 0, len2 = col.length; j<len2; j++){
  549. col[j].left = leftPosText;
  550. }
  551. }
  552. }
  553. //アクション関数を返す
  554. return function(e){
  555. var ct = e.currentTarget;
  556. var top = ct.scrollTop;
  557. var left = ct.scrollLeft;
  558. //行固定
  559. if(preTop != top){
  560. fixRows(top);
  561. preTop = top;
  562. }
  563. //列固定→インターバル処理
  564. nextLeft = left;
  565. };
  566. }
  567. /**
  568. *TableのCellSpacing
  569. *border-collapse:collapseが設定されていたら強制的に0pxとする
  570. *@param jqTbl 値を取得したいtblのjQueryオブジェクト
  571. *@return 得られたcell-spacing値の配列
  572. */
  573. function getBorderSpacing(jqTbl){
  574. if(jqTbl.css("border-collapse") == "collapse"){
  575. return [0, 0];
  576. }
  577. var bsText = jqTbl.css("border-spacing");
  578. var bsArr = bsText.split(" ");
  579. return [
  580. removePx(bsArr[0]),
  581. //For Opera
  582. bsArr[1] == undefined ? removePx(bsArr[0]) : removePx(bsArr[1])
  583. ];
  584. }
  585. /**
  586. *スクロールテーブル用のスタイルシートを追加する.
  587. *tableのもつレイアウト機能を全て殺してblock要素で再構築する.
  588. *@param jqTbl スクロールテーブル
  589. *@param tableWidth 固定幅
  590. *@param tableHeight 固定高さ
  591. *@param flozenF 固定したい左列の数(既定値0)
  592. *@param flozenL 固定したい右列の数(既定値0)
  593. */
  594. function appendStyle(
  595. jqTbl, tableWidth, tableHeight, flozenF, flozenL){
  596. //既存スタイルの削除
  597. removeStyleElem(jqTbl);
  598. //head要素に追加
  599. var style = getStyleString(
  600. jqTbl, tableWidth, tableHeight, flozenF, flozenL);
  601. appendStyleElem(jqTbl, style);
  602. }
  603. /**
  604. *スクロールテーブル用のスタイルシートの設定文字列を取得する.
  605. *@param jqTbl スクロールテーブル
  606. *@param tableWidth 固定幅
  607. *@param tableHeight 固定高さ
  608. *@param flozenF 固定したい左列の数
  609. *@param flozenL 固定したい右列の数
  610. */
  611. function getStyleString(
  612. jqTbl, tableWidth, tableHeight, flozenF, flozenL){
  613. var tblId = getId(jqTbl);
  614. var borderSpacings = getBorderSpacing(jqTbl);
  615. var borderSpacingX = borderSpacings[0];
  616. var borderSpacingY = borderSpacings[1];
  617. var styles = new Array();
  618. //全体をblock要素に
  619. styles.push(
  620. new styleBuilder(
  621. tblId,
  622. ["", "thead", "tbody", "tfoot", "tr", "td", "td"],
  623. {"display": "block"}
  624. )
  625. );
  626. //処理の都合上,td,th→tr→thead,tbody,tfoot→tableの順にスタイルを設定
  627. styles.push(
  628. new styleBuilder(
  629. tblId,
  630. ["th", "td"],
  631. {"position": "absolute","overflow":"hidden"}
  632. )
  633. );
  634. //列方向
  635. var i,len,row;
  636. var tds = jqTbl.find("tr:eq(0)").children();
  637. var left = borderSpacingX;
  638. for(i = 0, len = tds.length; i<len; i++){
  639. var td = jQuery(tds[i]);
  640. var o_width = td.outerWidth(true);
  641. var i_width = cellInnerWidth(td);
  642. styles.push(
  643. new styleBuilder(
  644. tblId,
  645. [">thead>tr>th:nth-child(" + (i + 1) + ")",
  646. ">tbody>tr>td:nth-child(" + (i + 1) + ")"],
  647. {
  648. "width": i_width + "px",
  649. "left" : left + "px",
  650. "top" : borderSpacingY + "px"
  651. }
  652. )
  653. );
  654. styles.push(
  655. new styleBuilder(
  656. tblId,
  657. [">tfoot>tr>td:nth-child(" + (i + 1) + ")"],
  658. {
  659. "width": i_width + "px",
  660. "left" : left + "px",
  661. "top" : 0
  662. }
  663. )
  664. );
  665. left += (borderSpacingX + o_width);
  666. }
  667. styles.push(
  668. new styleBuilder(
  669. tblId,
  670. ["tr"],
  671. {
  672. "position": "relative",
  673. "width": left + "px",
  674. "overflow": "hidden"
  675. }
  676. )
  677. );
  678. styles.push(
  679. new styleBuilder(
  680. tblId,
  681. [""],
  682. {
  683. "position": "relative",
  684. "width": left + "px", "height": "auto"
  685. }
  686. )
  687. );
  688. //行方向
  689. var thtr = jqTbl.find("thead tr");
  690. var throwHeight;
  691. var headerHeight = 0;
  692. if(thtr.length != 0){
  693. for(i = 0,len = thtr.length; i<len; i++){
  694. row = jQuery(thtr[i]);
  695. throwHeight = row.innerHeight() + borderSpacingY;
  696. styles.push(
  697. new styleBuilder(
  698. tblId,
  699. [">thead>tr:nth-child(" + (i+1) + ")"],
  700. {"height": throwHeight + "px"}
  701. )
  702. );
  703. styles.push(
  704. new styleBuilder(
  705. tblId,
  706. [">thead>tr:nth-child(" + (i+1) + ") > th"],
  707. {"height": cellInnerHeight(row.find("th:eq(0)")) + "px"}
  708. )
  709. );
  710. headerHeight += throwHeight;
  711. }
  712. }
  713. //body部は1行目の高さを共通の高さとする.
  714. var tbtr = jqTbl.find("tbody tr:eq(0)");
  715. var tbtd = tbtr.find("td:eq(0)");
  716. if(tbtr.length != 0){
  717. styles.push(
  718. new styleBuilder(
  719. tblId,
  720. [">tbody>tr"],
  721. {"height": (tbtr.innerHeight() + borderSpacingY) + "px"}
  722. )
  723. );
  724. styles.push(
  725. new styleBuilder(
  726. tblId,
  727. [">tbody>tr>td"],
  728. {"height": cellInnerHeight(tbtd) + "px"}
  729. )
  730. );
  731. }
  732. var tftr = jqTbl.find(">tfoot>tr");
  733. var tfrowHeight;
  734. var footerHeight = 0;
  735. if(tftr.length != 0){
  736. for(i = 0, len = tftr.length; i<len; i++){
  737. row = jQuery(tftr[i]);
  738. tfrowHeight = row.innerHeight() + borderSpacingY;
  739. styles.push(
  740. new styleBuilder(
  741. tblId,
  742. [">tfoot>tr:nth-child(" + (i+1) + ")"],
  743. {"height": tfrowHeight + "px"}
  744. )
  745. );
  746. styles.push(
  747. new styleBuilder(
  748. tblId,
  749. [">tfoot>tr:nth-child(" + (i+1) + ")>td"],
  750. {"height": cellInnerHeight(row.find("td:eq(0)")) + "px"}
  751. )
  752. );
  753. footerHeight += tfrowHeight;
  754. }
  755. }
  756. styles.push(
  757. new styleBuilder(
  758. tblId,
  759. [">thead"],
  760. {
  761. "position": "absolute",
  762. "top": 0,
  763. "z-index": 100,
  764. "overflow": "hidden"
  765. }
  766. )
  767. );
  768. styles.push(
  769. new styleBuilder(
  770. tblId,
  771. [">tfoot"],
  772. {
  773. "position": "absolute",
  774. "bottom": 0,
  775. "z-index": 100,
  776. "overflow": "hidden"
  777. }
  778. )
  779. );
  780. //NOTE:tbody部のpaddingでなければ下部空白を作ることができない.(opera,chrome)
  781. styles.push(
  782. new styleBuilder(
  783. tblId,
  784. [">tbody"],
  785. {
  786. "width": left + "px",
  787. "height": "auto",
  788. "padding-top": headerHeight + "px",
  789. "padding-bottom": footerHeight + borderSpacingY + "px",
  790. "padding-right": 0,
  791. "padding-left": 0
  792. }
  793. )
  794. );
  795. //固定列
  796. var selectors = new Array();
  797. var j;
  798. for(j = 1; j <= flozenF; j++){
  799. selectors.push(">thead>tr>th:nth-child(" + j + ")");
  800. selectors.push(">tfoot>tr>td:nth-child(" + j + ")");
  801. }
  802. for(j = 1; j <= flozenL; j++){
  803. selectors.push(">thead>tr>th:nth-last-child(" + j + ")");
  804. selectors.push(">tfoot>tr>td:nth-last-child(" + j + ")");
  805. }
  806. styles.push(
  807. new styleBuilder(
  808. tblId,
  809. selectors,
  810. {"z-index": 150}
  811. )
  812. );
  813. for(j = 1; j <= flozenF; j++){
  814. selectors.push(">tbody>tr>td:nth-child(" + j + ")");
  815. }
  816. for(j = 1; j <= flozenL; j++){
  817. selectors.push(">tbody>tr>td:nth-last-child(" + j + ")");
  818. }
  819. styles.push(
  820. new styleBuilder(
  821. tblId,
  822. selectors,
  823. {"z-index": 50}
  824. )
  825. );
  826. //table本体
  827. var pos = jqTbl.css("position");
  828. if(pos == "" || pos == "static"){
  829. pos = "relative";
  830. }
  831. styles.push(
  832. new styleBuilder(
  833. tblId,
  834. [""],
  835. {
  836. "position": pos,
  837. "overflow": "auto",
  838. "width": (tableWidth - borderSpacingX) + "px",
  839. "height": (tableHeight - borderSpacingY) + "px"
  840. }
  841. )
  842. );
  843. return styles.join("\n");
  844. }
  845. /**
  846. *テーブルのセルの内部幅を取得する.
  847. *@param 幅を取得したいtd,th要素のjQueryオブジェクト
  848. *@return cellpaddingが取り除かれた幅
  849. */
  850. function cellInnerWidth(cell){
  851. //NOTE:td,th要素においてはinnerWidth,innerHeightから得られた値にpaddingが含まれている.
  852. return cell.innerWidth()
  853. - removePx(cell.css("padding-left"))
  854. - removePx(cell.css("padding-right"));
  855. }
  856. /**
  857. *テーブルのセルの内部の高さを取得する.
  858. *@param 高さを取得したいtd,th要素のjQueryオブジェクト
  859. *@return cellpaddingが取り除かれた高さ
  860. */
  861. function cellInnerHeight(cell){
  862. return cell.innerHeight()
  863. - removePx(cell.css("padding-top"))
  864. - removePx(cell.css("padding-bottom"));
  865. }
  866. /**
  867. *テーブルのthead,tbody,tfootの順番を入れ替える
  868. *@param 入れ替え対象のテーブルのjQueryオブジェクト
  869. */
  870. function switchOrder(jqTbl){
  871. var thead = jqTbl.find("thead").remove();
  872. var tbody = jqTbl.find("tbody").remove();
  873. var tfoot = jqTbl.find("tfoot").remove();
  874. jqTbl.append(thead).append(tbody).append(tfoot);
  875. }
  876. })();
  877. /**
  878. *IE8用
  879. */
  880. var forIE8 = new (function(){
  881. /**
  882. *Tableにスクロール機能を追加する.
  883. *@param tbl 機能を追加する対象のtblエレメント
  884. *@param tableWidth テーブルの幅
  885. *@param tableHeight テーブルの高さ
  886. *@param flozenF 固定列(左)
  887. *@param flozenL 固定列(右)
  888. */
  889. this.addScrollFunction = function(
  890. tbl, tableWidth, tableHeight, flozenF, flozenL){
  891. var jqTbl = jQuery(tbl);
  892. //インターバル処理の削除
  893. clearInterval(~~jqTbl.attr(TIMER_ID));
  894. //2重のdivで囲む
  895. if(getScroller(jqTbl).length == 0){
  896. wrapTableByDivs(jqTbl);
  897. }
  898. //列方向のクラスを追加
  899. appendRowClass(jqTbl, flozenF, flozenL);
  900. //スタイルシートの削除
  901. removeStyleElem(jqTbl);
  902. //セルの内部divタグの追加
  903. appendDivs(jqTbl, flozenF, flozenL);
  904. //スタイルシートを追加
  905. appendStyle(
  906. jqTbl, tableWidth, tableHeight, flozenF, flozenL);
  907. //セルのポジションのリセット
  908. resetPosition(jqTbl);
  909. //イベントの追加
  910. addScrollEvent(jqTbl, flozenF, flozenL);
  911. };
  912. /**
  913. *スクロールイベントを追加する.
  914. *@param tbl 機能を追加する対象のtblエレメント
  915. *@param flozenF 固定列(左)
  916. *@param flozenL 固定列(右)
  917. */
  918. function addScrollEvent(jqTbl, flozenF, flozenL){
  919. var scroller = getScroller(jqTbl);
  920. scroller.unbind("scroll");
  921. scroller.bind(
  922. "scroll",
  923. getScrollAction(jqTbl, flozenF, flozenL));
  924. }
  925. /**
  926. *スクロール時の処理を取得する
  927. *@param tbl 機能を追加する対象のtblエレメント
  928. *@param flozenF 固定列(左)
  929. *@param flozenL 固定列(右)
  930. */
  931. function getScrollAction(jqTbl, flozenF, flozenL){
  932. var PX = "px";
  933. var tblId = getId(jqTbl);
  934. var scroller = getScroller(jqTbl);
  935. var fixer = getFixer(jqTbl);
  936. var barWidth = getScrollbarWidth();
  937. var scrollerWidth = scroller.innerWidth();
  938. var scrollerHeight = scroller.innerHeight();
  939. var fixerWidth = fixer.outerWidth();
  940. var fixerHeight = fixer.outerHeight();
  941. //右要素位置の差分
  942. var leftDiff = scrollerHeight > fixerHeight
  943. ? fixerWidth - scrollerWidth
  944. : fixerWidth - scrollerWidth + barWidth;
  945. //下要素位置の差分
  946. var topDiff = scrollerWidth > fixerWidth
  947. ? fixerHeight - scrollerHeight
  948. : fixerHeight - scrollerHeight + barWidth;
  949. var headElemStyles = toStyleArr(jqTbl.find("thead>tr>th"));
  950. var footElemStyles = toStyleArr(jqTbl.find("tfoot>tr>td"));
  951. var i;
  952. var selectors;
  953. selectors = new Array();
  954. for(i = 0; i < flozenF; i++){
  955. selectors.push("tr>td.f" + i + ",tr>th.f" + i);
  956. }
  957. var leftElemStyles = toStyleArr(jqTbl.find(selectors.join(",")));
  958. selectors = new Array();
  959. for(i = 0; i < flozenL; i++){
  960. selectors.push("tr>td.l" + i + ",tr>th.l" + i);
  961. }
  962. var rightElemStyles = toStyleArr(jqTbl.find(selectors.join(",")));
  963. var nextLeft = 0;
  964. var mooving = false;
  965. var preTop;
  966. var preLeft;
  967. fixRows(0);
  968. fixColumns(0);
  969. //列固定はインターバル処理とする
  970. var timerId = setInterval(function(){
  971. if(!mooving && preLeft != nextLeft){
  972. preLeft = nextLeft;
  973. mooving = true;
  974. fixColumns(nextLeft);
  975. mooving = false;
  976. }
  977. }, synchronizeInterval);
  978. jqTbl.attr(TIMER_ID, timerId);
  979. //アクション関数を返す
  980. return function(e){
  981. var top = scroller.scrollTop();
  982. var left = scroller.scrollLeft();
  983. //変化のあった方向のみ実行
  984. if(preTop != top){
  985. fixRows(top);
  986. preTop = top;
  987. }
  988. nextLeft = left;
  989. }
  990. /**
  991. *行を固定する
  992. *@param top 縦方向のスクロール量
  993. */
  994. function fixRows(top){
  995. var i, len, topText;
  996. topText = top + PX;
  997. for(i = 0, len = headElemStyles.length; i<len; i++){
  998. headElemStyles[i].top = topText;
  999. }
  1000. var b_top = top - topDiff;
  1001. topText = b_top + PX;
  1002. for(i = 0, len = footElemStyles.length; i<len; i++){
  1003. footElemStyles[i].top = topText;
  1004. }
  1005. }
  1006. /**
  1007. *列を固定する
  1008. *@param left 横方向のスクロール量
  1009. */
  1010. function fixColumns(left){
  1011. var i, len, leftText;
  1012. leftText = left + PX;
  1013. for(i = 0, len = leftElemStyles.length; i<len; i++){
  1014. leftElemStyles[i].left = leftText;
  1015. }
  1016. var r_left = left - leftDiff;
  1017. leftText = r_left + PX;
  1018. for(i = 0, len = rightElemStyles.length; i<len; i++){
  1019. rightElemStyles[i].left = leftText;
  1020. }
  1021. }
  1022. }
  1023. /**
  1024. *固定セルの内部をdivタグで囲む
  1025. *NOTE:ie8では背景色が意図した形でレンダリングされないため.
  1026. */
  1027. function appendDivs(jqTbl, flozenF, flozenL){
  1028. var cls = getDivClass(jqTbl);
  1029. //既存のdivを削除する
  1030. jqTbl.find("div." + cls).remove();
  1031. //ヘッダーとフッター
  1032. jqTbl.find("thead>tr,tfoot>tr").each(getWrapDiv("th,td"));
  1033. var selectors = new Array();
  1034. var i;
  1035. for(i = 0; i<flozenF; i++){
  1036. selectors.push("td.f" + i);
  1037. }
  1038. for(i = 0; i<flozenL; i++){
  1039. selectors.push("td.l" + i);
  1040. }
  1041. var selectorString = selectors.join(",");
  1042. jqTbl.find("tbody>tr").each(getWrapDiv(selectorString));
  1043. function getWrapDiv(cellSelector){
  1044. return function(i, elem){
  1045. var row = jQuery(elem);
  1046. var height = row.innerHeight();
  1047. row.find(cellSelector).each(wrapDiv);
  1048. function wrapDiv(i, elem){
  1049. var jq = jQuery(elem);
  1050. //border表示用のdiv
  1051. //NOTE:ie8ではposition:relativeが設定されているセルはbackground-colorがborderの全面にレンダリングされてしまう.
  1052. var borderDiv = jQuery("<div>").addClass(cls);
  1053. borderDiv.css({
  1054. "position": "absolute",
  1055. "width": jq.outerWidth(),
  1056. "height": jq.outerHeight(),
  1057. "top": -1 * removePx(jq.css("border-top-width")),
  1058. "left": -1 * removePx(jq.css("border-left-width")),
  1059. "border-top-width": jq.css("border-top-width"),
  1060. "border-top-style": jq.css("border-top-style"),
  1061. "border-top-color": jq.css("border-top-color"),
  1062. "border-right-width": jq.css("border-right-width"),
  1063. "border-right-style": jq.css("border-right-style"),
  1064. "border-right-color": jq.css("border-right-color"),
  1065. "border-bottom-width": jq.css("border-bottom-width"),
  1066. "border-bottom-style": jq.css("border-bottom-style"),
  1067. "border-bottom-color": jq.css("border-bottom-color"),
  1068. "border-left-width": jq.css("border-left-width"),
  1069. "border-left-style": jq.css("border-left-style"),
  1070. "border-left-color": jq.css("border-left-color")
  1071. });
  1072. jq.append(borderDiv);
  1073. }
  1074. }
  1075. }
  1076. }
  1077. /**
  1078. *スクロールテーブル用のスタイルシートを追加する.
  1079. *tableのもつレイアウト機能を全て殺してblock要素で再構築する.
  1080. *@param jqTbl スクロールテーブル
  1081. *@param tableWidth 固定幅
  1082. *@param tableHeight 固定高さ
  1083. *@param flozenF 固定したい左列の数(既定値0)
  1084. *@param flozenL 固定したい右列の数(既定値0)
  1085. */
  1086. function appendStyle(
  1087. jqTbl, tableWidth, tableHeight, flozenF, flozenL){
  1088. //head要素に追加
  1089. var style = getStyleString(
  1090. jqTbl, tableWidth, tableHeight, flozenF, flozenL);
  1091. appendStyleElem(jqTbl, style);
  1092. }
  1093. /**
  1094. *スクロールテーブル用のスタイルシートの設定文字列を取得する.
  1095. *@param jqTbl スクロールテーブル
  1096. *@param tableWidth 固定幅
  1097. *@param tableHeight 固定高さ
  1098. *@param flozenF 固定したい左列の数
  1099. *@param flozenL 固定したい右列の数
  1100. */
  1101. function getStyleString(
  1102. jqTbl, tableWidth, tableHeight, flozenF, flozenL){
  1103. var tblId = getId(jqTbl);
  1104. var fixerId = getFixerId(jqTbl);
  1105. var scrollerId = getScrollerId(jqTbl);
  1106. var styles = new Array();
  1107. //スクロール部
  1108. var position = jqTbl.css("position");
  1109. if(position == "" || position == "static"){
  1110. position = "relative";
  1111. }
  1112. styles.push(
  1113. new styleBuilder(
  1114. scrollerId,
  1115. [""],
  1116. {
  1117. "position": position,
  1118. "top": jqTbl.css("top"),
  1119. "left": jqTbl.css("left"),
  1120. "right": jqTbl.css("right"),
  1121. "bottom": jqTbl.css("bottom"),
  1122. "overflow": "auto",
  1123. //NOTE:IE8ではborderの分width,heightがずれてしまう
  1124. "width": tableWidth - removePx(jqTbl.css("border-right-width")) - removePx(jqTbl.css("border-left-width")) + "px",
  1125. "height": tableHeight - removePx(jqTbl.css("border-top-width")) - removePx(jqTbl.css("border-bottom-width")) + "px",
  1126. "border-top-width": jqTbl.css("border-top-width"),
  1127. "border-top-style": jqTbl.css("border-top-style"),
  1128. "border-top-color": jqTbl.css("border-top-color"),
  1129. "border-right-width": jqTbl.css("border-right-width"),
  1130. "border-right-style": jqTbl.css("border-right-style"),
  1131. "border-right-color": jqTbl.css("border-right-color"),
  1132. "border-bottom-width": jqTbl.css("border-bottom-width"),
  1133. "border-bottom-style": jqTbl.css("border-bottom-style"),
  1134. "border-bottom-color": jqTbl.css("border-bottom-color"),
  1135. "border-left-width": jqTbl.css("border-left-width"),
  1136. "border-left-style": jqTbl.css("border-left-style"),
  1137. "border-left-color": jqTbl.css("border-left-color"),
  1138. "margin-top": jqTbl.css("margin-top"),
  1139. "margin-right": jqTbl.css("margin-right"),
  1140. "margin-bottom": jqTbl.css("margin-bottom"),
  1141. "margin-left": jqTbl.css("margin-left")
  1142. }
  1143. )
  1144. );
  1145. //幅固定
  1146. styles.push(
  1147. new styleBuilder(
  1148. fixerId,
  1149. [""],
  1150. {
  1151. "position": "static",
  1152. "width": jqTbl.innerWidth() + "px"
  1153. }
  1154. )
  1155. );
  1156. //本体
  1157. styles.push(
  1158. new styleBuilder(
  1159. tblId,
  1160. [""],
  1161. {
  1162. "vertical-align": "top",
  1163. "position": "static",
  1164. "top": 0,
  1165. "left": 0,
  1166. "border": "none",
  1167. "margin": 0
  1168. }
  1169. )
  1170. );
  1171. //ヘッダ,フッタ
  1172. styles.push(
  1173. new styleBuilder(
  1174. tblId,
  1175. [">thead>tr>th", ">tfoot>tr>td"],
  1176. {
  1177. "position": "relative",
  1178. "overflow": "hidden",
  1179. "z-index": 25
  1180. }
  1181. )
  1182. );
  1183. //固定列
  1184. var i;
  1185. for(i = 0; i<flozenF; i++){
  1186. styles.push(
  1187. new styleBuilder(
  1188. tblId,
  1189. [">thead>tr>th.f" + i, ">tfoot>tr>td.f" + i],
  1190. {
  1191. "position": "relative",
  1192. "overflow": "hidden",
  1193. "z-index": 100
  1194. }
  1195. )
  1196. );
  1197. styles.push(
  1198. new styleBuilder(
  1199. tblId,
  1200. [">tbody>tr>td.f" + i],
  1201. {
  1202. "position": "relative",
  1203. "overflow": "hidden",
  1204. "z-index": 50
  1205. }
  1206. )
  1207. );
  1208. }
  1209. for(i = 0; i<flozenL; i++){
  1210. styles.push(
  1211. new styleBuilder(
  1212. tblId,
  1213. [">thead>tr>th.l" + i, ">tfoot>tr>td.l" + i],
  1214. {
  1215. "position": "relative",
  1216. "overflow": "hidden",
  1217. "z-index": 100
  1218. }
  1219. )
  1220. );
  1221. styles.push(
  1222. new styleBuilder(
  1223. tblId,
  1224. [">tbody>tr>td.l" + i],
  1225. {
  1226. "position": "relative",
  1227. "overflow": "hidden",
  1228. "z-index": 50
  1229. }
  1230. )
  1231. );
  1232. }
  1233. //NOTE:thead,tbody,tfootに透明を指定しないとセルの背景色が表示されない
  1234. styles.push(
  1235. new styleBuilder(
  1236. tblId,
  1237. [">thead", ">tbody", ">tfoot"],
  1238. {
  1239. "background-color": "transparent"
  1240. }
  1241. )
  1242. );
  1243. //NOTE:vertical-align:topとしないと基準がずれる
  1244. styles.push(
  1245. new styleBuilder(
  1246. tblId,
  1247. ["th", "td"],
  1248. {
  1249. "vertical-align": "top"
  1250. }
  1251. )
  1252. );
  1253. return styles.join("\n");
  1254. }
  1255. })();
  1256. /**
  1257. *IE用
  1258. */
  1259. var forIE = new (function(){
  1260. /**
  1261. *Tableにスクロール機能を追加する.
  1262. *@param tbl 機能を追加する対象のtblエレメント
  1263. *@param tableWidth テーブルの幅
  1264. *@param tableHeight テーブルの高さ
  1265. *@param flozenF 固定列(左)
  1266. *@param flozenL 固定列(右)
  1267. */
  1268. this.addScrollFunction = function(
  1269. tbl, tableWidth, tableHeight, flozenF, flozenL){
  1270. var jqTbl = jQuery(tbl);
  1271. clearInterval(~~jqTbl.attr(TIMER_ID));
  1272. //2重のdivで囲む
  1273. if(getScroller(jqTbl).length == 0){
  1274. wrapTableByDivs(jqTbl);
  1275. }
  1276. //列方向のクラスを追加
  1277. appendRowClass(jqTbl, flozenF, flozenL);
  1278. //スタイルシートを追加
  1279. appendStyle(
  1280. jqTbl, tableWidth, tableHeight, flozenF, flozenL);
  1281. //セルのポジションのリセット
  1282. resetPosition(jqTbl);
  1283. //イベントの追加
  1284. addScrollEvent(jqTbl, flozenF, flozenL);
  1285. }
  1286. /**
  1287. *スクロールイベントを追加する.
  1288. *@param tbl 機能を追加する対象のtblエレメント
  1289. *@param flozenF 固定列(左)
  1290. *@param flozenL 固定列(右)
  1291. */
  1292. function addScrollEvent(jqTbl, flozenF, flozenL){
  1293. var scroller = getScroller(jqTbl);
  1294. scroller.unbind("scroll");
  1295. scroller.bind(
  1296. "scroll",
  1297. getScrollAction(jqTbl, flozenF, flozenL));
  1298. }
  1299. /**
  1300. *スクロール時の処理を取得する
  1301. *@param tbl 機能を追加する対象のtblエレメント
  1302. *@param flozenF 固定列(左)
  1303. *@param flozenL 固定列(右)
  1304. */
  1305. function getScrollAction(jqTbl, flozenF, flozenL){
  1306. var PX = "px";
  1307. var tblId = getId(jqTbl);
  1308. var scroller = getScroller(jqTbl);
  1309. var fixer = getFixer(jqTbl);
  1310. var barWidth = getScrollbarWidth();
  1311. var scrollerWidth = scroller.innerWidth();
  1312. var scrollerHeight = scroller.innerHeight();
  1313. var fixerWidth = fixer.outerWidth();
  1314. var fixerHeight = fixer.outerHeight();
  1315. //右要素位置の差分
  1316. var leftDiff = scrollerHeight > fixerHeight
  1317. ? fixerWidth - scrollerWidth
  1318. : fixerWidth - scrollerWidth + barWidth;
  1319. //下要素位置の差分
  1320. var topDiff = scrollerWidth > fixerWidth
  1321. ? fixerHeight - scrollerHeight
  1322. : fixerHeight - scrollerHeight + barWidth;
  1323. var headElemStyles = toStyleArr(jqTbl.find("thead tr th"));
  1324. var footElemStyles = toStyleArr(jqTbl.find("tfoot tr td"));
  1325. var i;
  1326. var selectors;
  1327. selectors = new Array();
  1328. for(i = 0; i < flozenF; i++){
  1329. selectors.push("tr td.f" + i + ",tr th.f" + i);
  1330. }
  1331. var leftElemStyles = toStyleArr(jqTbl.find(selectors.join(",")));
  1332. selectors = new Array();
  1333. for(i = 0; i < flozenL; i++){
  1334. selectors.push("tr td.l" + i + ",tr th.l" + i);
  1335. }
  1336. var rightElemStyles = toStyleArr(jqTbl.find(selectors.join(",")));
  1337. var nextLeft = 0;
  1338. var mooving = false;
  1339. var preTop;
  1340. var preLeft;
  1341. fixRows(0);
  1342. fixColumns(0);
  1343. var timerId = setInterval(function(){
  1344. if(!mooving && preLeft != nextLeft){
  1345. preLeft = nextLeft;
  1346. mooving = true;
  1347. fixColumns(nextLeft);
  1348. mooving = false;
  1349. }
  1350. }, synchronizeInterval);
  1351. jqTbl.attr(TIMER_ID, timerId);
  1352. //アクション関数を返す
  1353. return function(e){
  1354. var top = scroller.scrollTop();
  1355. var left = scroller.scrollLeft();
  1356. //変化のあった方向のみ実行
  1357. if(preTop != top){
  1358. fixRows(top);
  1359. preTop = top;
  1360. }
  1361. nextLeft = left;
  1362. }
  1363. /**
  1364. *行を固定する
  1365. *@param top 縦方向のスクロール量
  1366. */
  1367. function fixRows(top){
  1368. var i, len, topText;
  1369. topText = top + PX;
  1370. for(i = 0, len = headElemStyles.length; i<len; i++){
  1371. headElemStyles[i].top = topText;
  1372. }
  1373. var b_top = top - topDiff;
  1374. topText = b_top + PX;
  1375. for(i = 0, len = footElemStyles.length; i<len; i++){
  1376. footElemStyles[i].top = topText;
  1377. }
  1378. }
  1379. /**
  1380. *列を固定する
  1381. *@param left 横方向のスクロール量
  1382. */
  1383. function fixColumns(left){
  1384. var i, len, leftText;
  1385. leftText = left + PX;
  1386. for(i = 0, len = leftElemStyles.length; i<len; i++){
  1387. leftElemStyles[i].left = leftText;
  1388. }
  1389. var r_left = left - leftDiff;
  1390. leftText = r_left + PX;
  1391. for(i = 0, len = rightElemStyles.length; i<len; i++){
  1392. rightElemStyles[i].left = leftText;
  1393. }
  1394. }
  1395. }
  1396. /**
  1397. *スクロールテーブル用のスタイルシートを追加する.
  1398. *tableのもつレイアウト機能を全て殺してblock要素で再構築する.
  1399. *@param jqTbl スクロールテーブル
  1400. *@param tableWidth 固定幅
  1401. *@param tableHeight 固定高さ
  1402. *@param flozenF 固定したい左列の数(既定値0)
  1403. *@param flozenL 固定したい右列の数(既定値0)
  1404. */
  1405. function appendStyle(
  1406. jqTbl, tableWidth, tableHeight, flozenF, flozenL){
  1407. //既存スタイルの削除
  1408. removeStyleElem(jqTbl);
  1409. //head要素に追加
  1410. var style = getStyleString(
  1411. jqTbl, tableWidth, tableHeight, flozenF, flozenL);
  1412. appendStyleElem(jqTbl, style);
  1413. }
  1414. /**
  1415. *スクロールテーブル用のスタイルシートの設定文字列を取得する.
  1416. *@param jqTbl スクロールテーブル
  1417. *@param tableWidth 固定幅
  1418. *@param tableHeight 固定高さ
  1419. *@param flozenF 固定したい左列の数
  1420. *@param flozenL 固定したい右列の数
  1421. */
  1422. function getStyleString(
  1423. jqTbl, tableWidth, tableHeight, flozenF, flozenL){
  1424. var tblId = getId(jqTbl);
  1425. var scrollerId = getScrollerId(jqTbl);
  1426. var fixerId = getFixerId(jqTbl);
  1427. var styles = new Array();
  1428. //スクロール部
  1429. var position = jqTbl.css("position");
  1430. if(position == "" || position == "static"){
  1431. position = "relative";
  1432. }
  1433. styles.push(
  1434. new styleBuilder(
  1435. scrollerId,
  1436. [""],
  1437. {
  1438. "position": position,
  1439. "top": jqTbl.css("top"),
  1440. "left": jqTbl.css("left"),
  1441. "right": jqTbl.css("right"),
  1442. "bottom": jqTbl.css("bottom"),
  1443. "overflow": "auto",
  1444. //NOTE:IEではborderの分width,heightがずれてしまう
  1445. "width": tableWidth - removePx(jqTbl.css("border-right-width")) - removePx(jqTbl.css("border-left-width")) + "px",
  1446. "height": tableHeight - removePx(jqTbl.css("border-top-width")) - removePx(jqTbl.css("border-bottom-width")) + "px",
  1447. "border-top-width": jqTbl.css("border-top-width"),
  1448. "border-top-style": jqTbl.css("border-top-style"),
  1449. "border-top-color": jqTbl.css("border-top-color"),
  1450. "border-right-width": jqTbl.css("border-right-width"),
  1451. "border-right-style": jqTbl.css("border-right-style"),
  1452. "border-right-color": jqTbl.css("border-right-color"),
  1453. "border-bottom-width": jqTbl.css("border-bottom-width"),
  1454. "border-bottom-style": jqTbl.css("border-bottom-style"),
  1455. "border-bottom-color": jqTbl.css("border-bottom-color"),
  1456. "border-left-width": jqTbl.css("border-left-width"),
  1457. "border-left-style": jqTbl.css("border-left-style"),
  1458. "border-left-color": jqTbl.css("border-left-color"),
  1459. "margin-top": jqTbl.css("margin-top"),
  1460. "margin-right": jqTbl.css("margin-right"),
  1461. "margin-bottom": jqTbl.css("margin-bottom"),
  1462. "margin-left": jqTbl.css("margin-left")
  1463. }
  1464. )
  1465. );
  1466. //幅固定
  1467. styles.push(
  1468. new styleBuilder(
  1469. fixerId,
  1470. [""],
  1471. {
  1472. "position": "static",
  1473. "width": jqTbl.innerWidth() + "px"
  1474. }
  1475. )
  1476. );
  1477. //本体
  1478. styles.push(
  1479. new styleBuilder(
  1480. tblId,
  1481. [""],
  1482. {
  1483. "vertical-align": "top",
  1484. "position": "static",
  1485. "top": 0,
  1486. "left": 0,
  1487. "border": "none",
  1488. "margin": 0
  1489. }
  1490. )
  1491. );
  1492. //ヘッダ,フッタ
  1493. styles.push(
  1494. new styleBuilder(
  1495. tblId,
  1496. [">thead>tr>th", ">tfoot>tr>td"],
  1497. {
  1498. "position": "relative",
  1499. "overflow": "hidden",
  1500. "z-index": 25
  1501. }
  1502. )
  1503. );
  1504. //固定列
  1505. var i;
  1506. for(i = 0; i<flozenF; i++){
  1507. styles.push(
  1508. new styleBuilder(
  1509. tblId,
  1510. [">thead>tr>th.f" + i, ">tfoot>tr>td.f" + i],
  1511. {
  1512. "position": "relative",
  1513. "overflow": "hidden",
  1514. "z-index": 100
  1515. }
  1516. )
  1517. );
  1518. styles.push(
  1519. new styleBuilder(
  1520. tblId,
  1521. [">tbody>tr>td.f" + i],
  1522. {
  1523. "position": "relative",
  1524. "overflow": "hidden",
  1525. "z-index": 50
  1526. }
  1527. )
  1528. );
  1529. }
  1530. for(i = 0; i<flozenL; i++){
  1531. styles.push(
  1532. new styleBuilder(
  1533. tblId,
  1534. [">thead>tr>th.l" + i, ">tfoot>tr>td.l" + i],
  1535. {
  1536. "position": "relative",
  1537. "overflow": "hidden",
  1538. "z-index": 100
  1539. }
  1540. )
  1541. );
  1542. styles.push(
  1543. new styleBuilder(
  1544. tblId,
  1545. [">tbody>tr>td.l" + i],
  1546. {
  1547. "position": "relative",
  1548. "overflow": "hidden",
  1549. "z-index": 50
  1550. }
  1551. )
  1552. );
  1553. }
  1554. //NOTE:他のブラウザとの挙動の統一
  1555. styles.push(
  1556. new styleBuilder(
  1557. tblId,
  1558. ["th", "td"],
  1559. {
  1560. "vertical-align": "top"
  1561. }
  1562. )
  1563. );
  1564. return styles.join("\n");
  1565. }
  1566. })();
  1567. })(jQuery);
  1568. /**
  1569. *ロード時の処理
  1570. */
  1571. jQuery(function(){
  1572. jQuery("table[scrollable=true]").scrollable();
  1573. });