第一次看到敏捷开发的定义,我就被敏捷开发迷住了。通俗来说,敏捷开发可以让我们用过的代码可以再次重用,因为是再次重用,所以相对安全,再次调试也没有第一次那么费心,省时省力。不断重用代码的过程中把存在的bug不断的修复,也因为不断的去重用, 这个模板变得起越来越独立,适用的情况越来越广范,最后在安全方面达到铜墙铁壁,在开发方面达到随心所欲,在维护方面达到从容面对。 敏捷开发的确是利害,但如何练就这种深奥的武功呢?就我自身的情况靠人传授武功是不可能了,因为公司就我一个做开发的,苦思幂想之后,决定从开源的优秀框架入手,把它一行一行代码看懂,然后再为我所用。因为是一个人开发,前台和后台都得包办,哪从那一面做起呢? 之前有过一二个月的开发经验,觉得前台的JS很费时,而且老觉得在做重复的事情,比如发ajax请求,接收结果之后操作节点(有时候遇到不兼容的情况,如select和table在IE下不支持使用innerHTML,style在IE不会自动转化为字符串,要用cssText代替,一旦这些情况遇上,真得是无比打击程序员的积极性,因为你要为此花时间去找替代方案,去调试),还有节点轮换,弹出层,表单验证等一系列烦琐工作。所以我就坚决从前台的JS做起。为了练就怎么把JS的重用性提高,我选择向Jquery取经。花了几个月看了一大半,略有所得。我其中之一的JS模块“无限深度操作节点”(文采不好,名字不妥别见怪)出来了。有了它,我在操作节点方面变得容易,代码变得精简,而且不用写额外的代码去兼容浏览器,谈笑中,功能就完成了。
首先我谈谈是什么让操作节点给我们带来烦恼:
编写ajax程序时,动态增删改页面元素几乎是不可避免的,使用属性innerHTML就是我们经常利用的途径,但在IE中table,thead,tfoot,tbody,tr,col,colgroup,html,title,style,frameset的innerHTML属性是只读的,也就是我们不能使用innerHTML更新这些节点。(这里没有提到select,其实select也是不会成功的,估计是IE的bug)。例子如下:(下面的$id代表document.getElementById)
jquery
//执行代码: $id('jkit').innerHTML = 'jquery ';//IE下并没有报任何错误,但select一个option节点都没有了。如果你对table使用innerHTML,IE会报unknown runtime error
对于这种情况比较常用的兼容方法是外加一个外层元素,例子如下:
jquery
//执行代码: $id('jkit').innerHTML = 'jkit '; //这样IE也成功改变select,但这种做法有个缺点,如果你对select注册过事件,这些事件会全部丢失,你要外加代码来重新注册事件。
在指定的节点前后新增节点,这就涉及于节点定位,节点创建以及给节点属性设置。使用innerHTML通常只用于覆盖DOM元素的所有节点,如果只想改变元素的某个子节点,或者只想在某个子节点前后增加节点,仍然使用innerHTML就适得其反,实现起来很吃力了,而且使用了innerHTML之后,对子节点注册过的事件肯定全部丢失掉。不使用innerHTML,那只好使用原生的DOM方法了,但这种代替方案也不好使,看下面例子:
jquery jkit mars
//现在我想在jkit之前多加一个option,用原生的DOM方法实现: var newNode = document.createElement('option') ,//新建一个节点 selector = document.getElementById('jkit3') ,/* 也可以用selector.options,但getElementsByTagName更通用。 那用childNodes怎么?最好也不要,对于空白节点,IE和FF的处理方式不一样, 就这例子,在FF中,select的firstChild是空白文本节点, 因为select和第一个option之间有换行以及空白字符, FF会为其创建节点,而IE会忽略 */ options = document.getElementsByTagName('option'); newNode.setAttribute('value','new'); //newNode.setAttribute('text','NewNode');text不支持这样设置 //newNode.text = 'NewNode';ie不支持这种方式 newNode.innerHTML = 'NewNode'; selector.insertBefore(newNode,options[1]); //在kit之前插入
上記のコードを実行すると、select にはもう 1 つのオプションがあります: jquery ノードを追加するには 6 行のコードが必要です。では、バッチ操作では何をすればよいでしょうか。さらに、ネイティブ DOM メソッドは名前が長く、パラメータの順序を覚えるのが困難です (開発者が毎日ノードを操作し、これらのメソッドを毎日使用している場合を除く)。 )。この 6 行のコードをメソッドに記述し、パラメーターを渡すことでバッチ操作を完了できるので、それほど面倒ではないという人もいます。ある程度の業務負担は軽減できますが、長期的な視点で考えるとあまり現実的ではありません。理由は 3 つあります。まず、新しく追加された各ノードの属性が異なり、追加された位置も異なります。メソッドの外側で新しいノードの位置を決定し、メソッド内でこのノードにどの属性を追加するかを決定する必要があります。次に、上記のコードからわかるように、ノードを追加するときに次のことを行う必要があります。属性に関しては、各要素には異なる属性があり、互換性スキームに違いがあるため、さまざまな要素に対してこのようなメソッドを記述する場合は、各要素の互換性スキームを覚えておく必要があります。しかし、それらをすべて覚えられると誰が言えるでしょうか? 互換性のあるソリューションであり、プログラマーがテクノロジーで違いを生み出したい場合は、記憶力ではなく思考ロジックの向上に依存します。第三に、この分野で懸命に働く必要はありません。あるプロジェクトがその関数を実装した後、次のプロジェクトは同じような関数を返しますが、細部は異なります。これは、特定の詳細を変更する必要があるため、コードの再利用性が非常に低いことが想像できます。自分が書いたロジックを再理解して、コードを別の方法でリファクタリングし直すと、効率が非常に低くなり、アジャイル開発はまったく行われません。 これを見て、読者はおそらく、これが行われていない場合、開発は本当にコピーアンドペーストできるのかと疑問に思うでしょう。それは不可能であると断言できます。コードがどれほど包括的であっても、異なる包括的なロジックを実装することは不可能です。ただし、高度に再利用可能にすることができます。重要なのは、さまざまなロジックの共通点を見つけて、それらを独立させることです。上記の演算ノード方式が再利用性が低いと言う理由は、演算ノードに特定のロジックが混在している、つまり特定のロジックのコードが演算ノードのコードと密接に関係しすぎているためです。ノードを操作する際に、前回のコードを再利用したい場合は、前回作成したロジックを見直して、ノードを操作するコードを分離してから使用する必要があります。以下に紹介する無限レベルのノード操作は論理的に非常に独立しており、ロジックとは何の関係もありません。
上面的以select为例子,option是select的孩子节点,我们操作的是两层的树型结构,如果再深入一些,操作元素的子孙节点呢?如何定位元素后代节点呢?下面还是以原生的dom方法来实现:
//现在我想在第一个li之前多加一个option,用原生的DOM方法实现: newNode = document.createElement('li'), table = document.getElementById('jkit4'), //取li的父节点: uls = table.getElementsByTagName('ul'), /* getElementsByTagName虽然通用,但如果li标签里面嵌套了li,li父节点的兄弟节点也有li的话,那么getElementsByTagName都会取到这些节点,如果你的html结构真有哪么复杂,取出来结果后你也很难定位到你想找的li节点。遇到这情况,你只以通过childNodes一层层往下找,但之前提过childNodes在IE和FF中行为是不致的,所以你还要做兼容处理。*/ lis = table.getElementsByTagName('li'); newNode.innerHTML = 'NewNode'; //在指定位置插入 uls[0].insertBefore(newNode,lis[0]);
万变不离其中,跟上面的实现差不多,遇到复杂的html结构,可能就是定位比较麻烦,有人说定位不难,我可以在想要找的li上给定一个id属性,这样再怎么复杂也可以一步定位,但我想问如果要批量操作呢? 如果table有很多行,每一行第都涉及到这样的操作,那是不是每个li给定一人id ? 是可以,通过循环指也不怎么难,但我是反对这样的做法。如果有好的代替方案,我更赞成的是保持干净的HTML,在我开发的项目中,用于定位无素属性我是尽可能少给,因为我有足够灵活的方案寻节点。说到干净的HTML,这里说点题外话。怎么保持HTML的干净呢?在我开发中,HTML是不会混杂任何 逻辑性的javascript代码(只有接收后台数据的一些变量,而且这些变量统一放在HTML的最后面,变量太多的话,以组的形式组织),给元素注册事件也不会把onclick,onblur等事件代码嵌进html中,查看源码你不会在HTML中看到任何onclick,onblur等事件代码。回到这篇文章的主题吧!下面就这三点谈谈怎么让我们的工作变得简单!
次に話したいのは、イベントをシンプルにするコードの書き方です。現時点では技術的なイベントは無視してください。このような JS クラスが存在する場合、その中には上記のイベントと、上記よりも複雑なイベントを完了するのに十分なメソッドがあります。この仮想的に存在するクラスにはメソッド名のみがあり、特定の実装がないため、これをインターフェイスと呼びます。次に説明するのは、インターフェイスの定義です。インターフェイス内のメソッドがニーズを満たすことができると判断したら、それを実装します。 (これはより良い開発プロセスであり、最初に計画を立ててから行動します)
オブジェクトのインスタンス化 開発プロジェクトには、回転、ページング、フォーム検証など、b2c および b2b で一般的に使用されるプラグインを含む、無限レベルのノード操作プラグインだけではありません。 、およびフォーム要素の取得。値の設定、バッチでの画像のアップロード、イベントの再パッケージ化、イベントのバッチ処理、検索の自動完了、ショッピング カートでの Ajax 操作、およびパブリック メソッドを使用したクラス。したがって、js 全体には多数のクラスが存在し、各クラスは js 変数に対応します。このように、開発中に同じ名前の変数が他の場所で誤って宣言された場合、このクラスが多数存在します。消えてしまいます。この状況を回避するために、すべてのクラスを $jkit という名前のクラスにカプセル化して、パブリック変数が 1 つだけになるようにしました。プラグイン クラスはローカルになりました。どのようにアクセスすればよいですか?いくつかのメソッドを定義する別の $CL クラスを定義します。これらのメソッドは、プラグインの数に関係なく、$CL と $jkit の 2 つのパブリック変数のみになります。 。 $CL は $jkit を呼び出す責任があります。たとえば、$CL の newObj は、プラグイン オブジェクトのメソッドをインスタンス化するために使用されます。 newObj には 2 つのパラメータがあり、最初のパラメータはインスタンス化するプラグインを指定します。2 番目のパラメータは、プラグインをインスタンス化するときに、配列の形式で渡されます。
/* 以下のコードは、新しいオブジェクトを再構築することに相当する、無制限の層のオブジェクトのオブジェクトを示しています。新しいオブジェクト バンドには、新しい作成方法があります。新しいオブジェクトの参照は table であり、table も新しいルートです。その後続オブジェクトは 2 番目のパラメータchilds によって決定されます。新しいルート参照は、後続オブジェクトを操作するためのエントリです。childs は次の 2 番目のパラメータです。数値グループの最初の要素は tr であり、dom ルート ルートの子ポイント tr が存在しない場合、新しいオブジェクトは構築されません。tr 内に tr が存在する場合、オブジェクトは再構築されません。 、 2 番目の要素は td 番目、つまり 2 つあるということですか? th は、新しいオブジェクトを構築すると同時に削除され、スペースで区切られ、検出される順序が制限されない場合には、td であってもよい。第三个元素为select ul,为什么这两个可以写在一起?因为他们位于同一层次的,相对于根节点,它们都在第三层。只要同一层次的都可以写在一起。后面的以此类推,数量不限,就是无限层次了。新对象树的层次结构和原dom树的层次结构是一一对应的。 */ root = document.getElementById('category'), childs = ['tr', 'td th', 'select ul', 'li option'], table = $CL.newObj('maNode', [root, childs]);
新しいツリーのメンバーメソッド、次の API を読むときは、2 つの点に注意してください。まず、ルートオブジェクトと子孫オブジェクトのすべてのメソッドは、元の DOM オブジェクトに対するものです (例: del を呼び出すなど)。新しいオブジェクト。その本質は、対応する元の DOM オブジェクトを削除することです。次に、オブジェクトの追加、削除、または変更が呼び出されるたびに、対応するブランチが再構築されます
ルートオブジェクトの固有メソッド
function map(index1,index2,,,indexN){} このメソッドは、子孫ノードを検索するために使用されます。 table.map(1,1,0) は、次の行を検索します。 2 つのセルの最初のオブジェクトは、select に対応するオブジェクトです。 Map にパラメータが 1 つだけあり、そのパラメータが DOM ネイティブ オブジェクトである場合、このメソッドは対応する新しいオブジェクトを返します。
function Index(DOMElement){} このメソッドは、ネイティブ DOMElement オブジェクト table.index(document.getElementById('lior')) に対応するインデックスを返し、[1, 2 ,0,0]、結果は配列形式になります
子孫オブジェクトに固有のメソッド
function add(index, html){} このメソッドは、兄弟ノードを追加するために使用されます。Index は、このメソッドを呼び出すオブジェクトの位置に対する相対的な変位です。挿入するノード、html には、W3c 標準に準拠する任意の HTML 文字列を指定できます。 < /td>gt;gt; gt;')、3 行目の前に新しい行を追加します (複数行を同時に挿入できます) table.map(2) .add(-2, ' ')、新しい行を追加します3 行目の前の行 table.map(0).add(2,'< ;/td> table.map(1).add(0,'< /td>gt ; gt; gt;')、インデックス 0 は、現在の行の前に新しい行を追加し、現在の行を削除することを意味します table.map(1).add( ' ')、最初のパラメータを省略します。これは特別な使用法です。 , どのオブジェクトが使用されているかに関係なく、新しい行が最後の行 table.map(1,1).add(1,'New cell を決定します。
function del(index){} このメソッドは兄弟ノードを削除するために使用されます。index は、このメソッドを呼び出すオブジェクトの位置を基準とした変位です。 table.map( 1) .del()、インデックスを省略することは、それ自体を削除することを意味します。ここでの 2 行目の削除は、table.map(1).del(0) table.map(0).del(2) と同等です。現在の呼び出しに関連して、オブジェクトの後の 2 行目、ここでは 3 行目 table.map(2).del(-2) を削除します。ここでは、現在の呼び出しオブジェクトの前の 2 行目を削除します。 、ここでは最初の行 table .map(0,1).del([0,-1,1]) を削除します。index が配列の場合、指定されたインデックスの兄弟ノードが削除されます。今回は、どのオブジェクトが呼び出されるかは関係ありません。負のインデックスは最後の Counting から開始することを意味し、-1 は最後の Counting を意味します。ここでは最初、2 番目、最後の th table.map(0,1) を削除します。 del(0,-1)、パラメータが 2 つある場合は、区間の兄弟ノードを指定します。このとき、どのオブジェクトを呼び出すかは関係ありません。負のインデックスは最後から数えることを意味します。ここでは、最初から最後までの要素を削除します。サイズの順序に制限はありません。
function getParent(){}
呼び出し元オブジェクトの親オブジェクトに対応するネイティブ DOM オブジェクト ノードを取得します。table.map(0,1).getParent().tagName は tr です。
function getHigher(){}
呼び出し元オブジェクトの親オブジェクトを取得します。table.map(0,1).getHigher.getNode().tagName は tr です。
ルート オブジェクトと子孫オブジェクトが所有するメソッド
function getNode(){}
呼び出し元オブジェクトに対応するネイティブ DOM オブジェクト ノードを取得します。table.getNode().tagName はテーブル、table.map(0,1).getNode () 番目の
function sizeOf(){}
呼び出し元オブジェクトのサブオブジェクトの数を取得します。 table.sizeOf() は 3 です。これは、行が 3 つあることを意味します。
function pos(){}
すべての兄弟ノードにおける呼び出し元オブジェクトの位置を取得します。table.map(1).pos() は 1 です。
function html(html){}
呼び出し元のオブジェクトに対応するネイティブ DOM オブジェクトの innerHTML を取得します。パラメータが渡された場合は、その innerHTML 属性に値を割り当てます。読み取り専用オブジェクト innerHTML に値を割り当てます)
関数 attr(html){} 呼び出し元のオブジェクトに対応するネイティブ DOM オブジェクトの innerHTML を取得します。パラメータが渡された場合は、対応する属性に値を割り当てます (まだ実装されていません)。
function before(index,html){} 呼び出し元オブジェクトの指定されたサブオブジェクトの前にノードを追加します。index は相対的な変位です。 table.before(1 ,' ') という行を追加します。 table.map (1 ,2,0).before(-1,'新しい li ノード ')、最後の li の前に新しい li を追加します (インデックスは負の数は最後から数えることを意味し、-1 は最後を意味します) table.before('< /td> ; ')、最初のパラメータを省略すると、最初のサブオブジェクトの前に新しいノードが追加されることになります。
function append(index,html){} 呼び出し元オブジェクトの指定されたサブオブジェクトの背後にノードを追加します。index は相対変位です。 table.append(1,' gt;
gt;gt;
gt;
table の後に行を追加します.map(1 ,2,0).append(-1,'新しい li ノード ')、最後の li (インデックス) の後に新しい li を追加します。負の数は最後から数えることを意味し、-1 は最後を意味します) table.append(' ')、最初のパラメータを省略すると、最初のサブオブジェクトの後に新しいノードを追加することになります。
function replace(index,html){} 呼び出し元オブジェクトの指定されたサブオブジェクトに対応するネイティブ DOM ノードを、HTML によって生成されたノードに置き換えます。インデックスは相対ディスプレイスメントです。 🎜>table.replace( 2,'改行 '), add新しい行を使用して、2 行目を置き換えます table.replace(-1,'New line < /td> ; ')、新しい行を追加し、最後の行をその行に置き換えます (負のインデックスは最後から数えることを意味し、-1 は最後の行を意味します)
function clean(index){} このメソッドは、兄弟ノードを削除するために使用されます。Index は、このメソッドを呼び出すオブジェクトの位置を基準とした変位です。 ()、インデックスの省略は、最初のサブオブジェクトの削除を意味します。ここでの最初の行の削除は、table.map(1).del(0) table.clean(2) と同じです。ここでは、3 行目の削除を意味します。 🎜>table.clean (-2)、ここでは最後の行を削除することを意味します table.map(0).clean([0,-1,1])、index が配列の場合、そのサブオブジェクト指定されたインデックスが削除され、インデックスが負の数の場合は最後から数えることを意味し、-1 は最後のものを意味します。ここでは最初、2 番目、最後の th table.map(0).clean(0,-) を削除します。 1) パラメータが 2 つある場合は、指定された範囲のサブオブジェクトを削除することを意味します。-1 は、最初から最後の要素を削除することを意味します。最初のパラメータとして使用できます。サイズオーダーに制限はありません
新しいツリーのルート ノードが table で、その子ノードが tbody/thead/tfoot の場合、これらのノードを頻繁に操作するのではなく、tr を直接操作するため、わずかに許可されたスルーを作成しました。これらのノードの処理。もちろん、tbody を操作したい場合は、['tbody thead tfoot', 'tr', 'td'] のようにパラメータを渡すことができます。そのうちの 1 つだけを取得したい場合は、['tbody' , 'tr', 'td ']; tr を直接取得する場合は、['tr','td'] を実行すると、tbody/thead/tfoot 内のすべての tr に対して新しいオブジェクトが生成されます。 >
結論: 考えてみれば、このようなプラグインがあれば、ノードの操作は簡単になります。前述したノード操作の 3 つの主要な問題は解決されます。また、このプラグインはロジックとは一切関係がないので、手に入れたらすぐに使用することができ、必要に応じて拡張することもできます。いつか開発プロセスがジグソーパズルのようになったら、開発したプラグインを組み合わせてプロジェクトが出来上がると想像してみてください。結果は期待したほど良くないかもしれませんが、この方向に進むと、イベントがますますシンプルになることは避けられません。記事が長くなってしまったので、ソースコード部分については次回記事を公開するときに詳しく説明します。
このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。
著者別の最新記事
2024-10-22 09:46:29
2024-10-13 13:53:41
2024-10-12 12:15:51
2024-10-11 22:47:31
2024-10-11 19:36:51
2024-10-11 15:50:41
2024-10-11 15:07:41
2024-10-11 14:21:21
2024-10-11 12:59:11
2024-10-11 12:17:31