ホームページ > ウェブフロントエンド > jsチュートリアル > jQueryソースコードのイベント解析解析_jquery

jQueryソースコードのイベント解析解析_jquery

WBOY
リリース: 2016-05-16 18:25:27
オリジナル
1304 人が閲覧しました

イベントの操作は、addEvent、fireEvent、removeEvent の 3 つのイベント メソッドにほかなりません。一般に、lib は、互換性メモリ リークなどの問題を解決するために、ブラウザによって提供される関数にいくつかの拡張を加えます。 3 番目の質問は、domReady のステータスを取得する方法です。
6.1 イベントパッケージ

ブラウザイベントの互換性は頭痛の種です。 IE のイベントはグローバル ウィンドウの下にありますが、Mozilla のイベントはイベント ソース パラメーターがコールバック関数に渡されることです。他にも多くのイベント処理メソッドがあります。

Jquery はイベント パッケージを提供します。これは他のライブラリが提供するものよりも少し単純ですが、使用するには十分です。

コードをコピー コードは次のとおりです:

// イベントをラップします。
修正: function(event) {
if (event[expando] == true) return event;// イベントがラップされたことを示します
// 元のイベントを保存し、そのイベントを複製します。
varoriginalEvent = イベント; ①
イベント = {originalEvent : originalEvent};
for (var i = this.props.length, prop;i;) {
-i]; event[prop] =originalEvent[prop]; }
event[expando] = true;event.preventDefault = function() { ②
// 元のイベント
if (originalEvent. PreventDefault)
originalEvent.preventDefault();
originalEvent.returnValue = false;
Event.stopPropagation = function() {

};正しい timeStamp
event.timeStamp =event.timeStamp || now();
// 正しいターゲット
if (!event.target) ③
target =event.srcElement ||
if (event.target.nodeType == 3)//テキストノードが親ノードです。
Event.target =event.target.parentNode;
// relationshipTarget
if (!event.popularTarget &&event.fromElement)event.popularTarget =event.fromElement ==event.target
? .toElement :event.fromElement;
// 欠落している場合は pageX/Y を計算し、clientX/Y が利用可能
if (event.pageX == null &&event.clientX != null) { ⑥
var doc = document.documentElement, body = document.body;
event.pageX =event.clientX
(doc && doc.scrollLeft || body && body.scrollLeft || 0)
- (doc .clientLeft || 0) 0);
event.pageY =event.clientY
(doc && doc.scrollTop || body && body.scrollTop || 0)
- (doc.clientTop || 0);

// キーイベントのどれを追加します
if (!event.that && ((event.charCode ||event.charCode === 0) ⑦
?event.charCode :event.keyCode ))
event.that =event.charCode ||event.keyCode;

// Mac 以外のブラウザにメタキーを追加します
if (!event.metaKey &&event.ctrlKey ) ⑧
event.metaKey =event.ctrlKey;
// クリック用に次を追加します: 1 == left; 3 == right
// 注: ボタンは正規化されていないため、使用しないでください。 it
if (!event.that &&event.button) ⑨
event.that = (event.button & 1 ? 1 : (event.button & 2
? 3 : (event.button & 4) ? 2 : 0)));
}、



上記のコードは、①で元のイベントの参照を保持し、元のイベントを複製します。このクローン イベントをラップします。 ② 元のイベントに対して、preventDefault メソッドと stopPropagation メソッドを実行して、デフォルトのイベント アクションの発生を防止するかどうか、およびバブリング イベントの上方への送信を停止するかどうかを決定します。

③IEでsrcElementを使用する場合、同時にテキストノードイベントの場合、ターゲットを親ノードに渡すように修正しました。

④relationTarget はマウスアウトとマウスオーバーにのみ役立ちます。 IE では to と from の 2 つの Target 変数に分割されますが、Mozilla では分割されません。互換性を確保するために、relationalTarget を使用して統合します。

⑥はイベントの座標位置です。これはページに相対的なものです。ページをスクロールできる場合は、クライアントにスクロールを追加する必要があります。 IE では、デフォルトの 2px の本文境界線も減算する必要があります。

⑦のポイントは、キーボードイベントのキーをevent.thatの属性に統一することです。 Ext での実装は ev.charCode || ev.keyCode 0; で、event.that にマウス イベント キーを統合します。 charCode と ev.keyCode の一方は文字キーであり、もう一方は文字キーではありません。 ⑨ & メソッドを使用して互換性を処理します。 Ext は、次の 3 行で互換性の問題を解決します。

var btnMap = Ext.isIE ? {1:0,4:1,2:2} : (Ext.isSafari ? {1:0,2:1,3:2} : {0:0 ,1:1,2:2}); this.button = e.button ? btnMap[e.button] : (e.this ? e.that-1 : -1); ①②③④⑤⑥⑦⑧⑨⑩ >
6.2 イベント処理

Jquery は、イベントを登録、削除、起動するためのメソッドをいくつか提供します。

6.2.1 登録

イベントを登録するために、jquery はバインド、ワン、トグル、ホバーの 4 つのイベント登録メソッドを提供します。バインドは最も基本的なメソッドです。 1 つは 1 回だけ実行するように登録されているメソッドで、toggle は交互に実行するように登録されているメソッドです。ホバーはマウスホバリングを登録する方法です。



コードをコピー コードは次のとおりです: bind : function(type, data, fn ) {
Return type == "unload" ? this.one(type, data, fn) : this
.each(function() {// fn || data, fn && data は data パラメータを認識しますはオプションです
jQuery.event.add(this, type, fn || data, fn && data)
}); unload イベントは 1 回のみ実行でき、他のイベントはデフォルトの登録方法を使用します。

// ワンタイム イベント ハンドラー関数を、一致する各要素の特定のイベント (クリックなど) にバインドします。
//各オブジェクト上で、このイベント ハンドラーは 1 回だけ実行されます。その他のルールはbind()関数と同じです。
// このイベント ハンドラーは、デフォルトの動作 (ブラウザー) を防ぐために使用できるイベント オブジェクトを受け取ります。
// デフォルトの動作をキャンセルしてイベントのバブリングを防止したい場合、このイベント処理関数は false を返す必要があります。




コードをコピー


コードは次のとおりです:
jQuery.event.add(this, type, one, fn &&データ);
});


One と binding は基本的に同じです。違いは、jQuery.event.add を呼び出すときに、登録されているイベント処理関数が少し調整されることです。 1 つは、受信イベント処理関数をプロキシするために jQuery.event.proxy と呼ばれるものです。イベントによってこのエージェントの関数の呼び出しがトリガーされると、まずイベントがキャッシュから削除され、次に登録されたイベント関数が実行されます。 fn で登録したイベント関数の参照を取得するクロージャの適用例です。

//ホバー イベント (マウスがオブジェクト上を移動し、オブジェクトから移動する) をシミュレートするメソッド。
//これは、頻繁に使用されるタスクに「保持」状態を提供するカスタム メソッドです。
//マウスが一致する要素上に移動すると、指定された最初の関数がトリガーされます。マウスがこの要素の外に出ると、
/ は指定された 2 番目の関数をトリガーします。さらに、マウスがまだ特定の要素 (div 内の画像など) にあるかどうかの検出も伴います。
// もしそうであれば、トリガーせずに「ホバー」状態を維持し続けます。 move-out イベント (mouseout イベントを使用した一般的なエラーを修正)。
ホバー: function(fnOver, fnOut) {
return this.bind('mouseenter', fnOver).bind('mouseleave', fnOut);
},



ホバーはバインドに基づいています。

//クリックするたびに関数を順番に呼び出します。
toggle: function(fn) {
var args = argument, i = 1;
while (i jQuery.event.proxy ( fn, args[i ]);//変更されたものは args
return this.click(jQuery.event.proxy(fn, function(event) {//Assign GUID this.lastToggle = (this. lastToggle || 0) % i; // 前の関数 events.preventDefault(); // デフォルトのアクションを防止します
// パラメータで関数を実行し、配列のようなパラメータを使用できます
return args[this. lastToggle].apply(this,arguments)||false;
}));
},

Toggle のパラメータは複数指定できます。まず、それらをコーディングして UUID を生成します。次に、 click メソッドを呼び出して、エージェントのコールバックを再度登録します。この関数は、イベントがトリガーされると実行され、最初にパラメーター内の関数が最後に実行された時刻が計算されます。次に、デフォルトのアクションを防止します。次に、実行する次の関数を見つけます。

//一般的に使用されるイベント メソッドを jquery オブジェクトに追加します
jQuery.each(
("blur, focus,load,size,scroll,unload,click,dblclick,"
"mousedown ,mouseup,mousemove,mouseover,mouseout,change,select,"
"submit,keydown,keypress,keyup,error").split(","),
function(i, name) {jQuery.fn [name] = function(fn) {
return fn? this.bind(name, fn) : this.trigger(name);

イベント処理メソッドには、上記のクリックが含まれます。ここでは、登録するためにbindがまだ呼び出されていることがわかります。もちろん、プログラムを通じてイベントをトリガーすることもできます。



上記のメソッドの多くはイベントを登録するもので、最終的には jQuery.event.add(); に分類され、登録関数が完了します。 Dom0 または DOM1 のイベント メソッドを使用する場合は、elem.onclick=function(){} を使用して、要素の特定のイベントに対するハンドラー関数を登録します。この最大の欠点は、各イベントが単なる処理関数であることです。 dom1 メソッドが改良され、elem.addEventListener(type, handle, false) を使用して要素イベントの複数のハンドラー関数を登録できるようになりました。

この処理方法は完璧ではありません。このイベントを一度だけ実行するのは少し面倒です。イベントのリスニングをキャンセルするには、イベント処理関数の最後に elem.removeEventListener を実行する必要があります。ビジネス上の問題が発生する可能性があります。イベントのリスニングをキャンセルする前に、最初のイベント処理関数が再度トリガーされた場合はどうなるでしょうか?

ブラウザによる方法もありますが、これはカスタムイベントの登録と処理をサポートしておらず、複数のイベントに同じハンドラー関数を登録することができません。

コードをコピー コードは次のとおりです:

jQuery.event = {//要素にイベントを追加します。
add : function(elem, Types, handler, data) {
if (elem.nodeType == 3 || elem.nodeType == 8) return; // 空のノードまたはコメント
// IE window に渡すことはできないので、最初にコピーしてください。
if (jQuery.browser.msie && elem.setInterval) elem = window;
// ハンドラーにグローバルに一意の ID を割り当てます
if (!handler.guid) handler.guid = this.guid;
// handler.data にデータを添付します
if (data != unknown) { ①
var fn = handler
handler =this.proxy(fn,function(){return fn .apply; (this,arguments);});
handler.data = data;
}
// 要素のイベントを初期化します。 events の値が取得できない場合は、データを初期化します: {} ②
var events =jQuery.data(elem,"events")||jQuery.data(elem,"events",{}),
// handle の値が取得できない場合は data を初期化する: function() {....} ③
handle = jQuery.data(elem, "handle")|| jQuery.data(elem, "handle") ,
function() {//トリガーの 2 番目のイベントを処理し、ページがアンロードされた後にイベントを呼び出します。
if (typeof jQuery != "unknown"&& !jQuery.event.triggered)
Return jQuery.event.handle.apply(//callee.elem=handle.elem
argument.callee.elem, argument);
}); // elem をハンドル属性として追加、メモリを防止しますローカル イベントがないため、IE でリークが発生します。
handle.elem = elem;
// 複数のイベント名を区切るにはスペースを使用します (例: jQuery(...).bind("mouseover Mouseout", fn);
jQuery.each(types. split(/s /), function(index, type) { ④
// 名前空間イベントは通常は使用されません。
var Parts = type.split(".");type = Parts[. handler.type = Parts[1];
// この要素の type イベントにバインドされたすべてのハンドラー
var handlers = events[type]; ⑤
if (!handlers) {// イベントを初期化します。ハンドラーリストが見つからない場合はキュー
handlers = events[type] = {}
// type が準備できていないか、ready のセットアップ実行が false を返す場合 ⑥
if (!jQuery.event; .special[type]|| jQuery.event.special[type].setup
.call(elem, data) === false) {// システムイベント関数を呼び出してイベントを登録します
if(elem) .addEventListener )elem.addEventListener(type,handle,false);
else if (elem.attachEvent)elem.attachEvent("on" type, handle);
}
}
//ハンドラー ID とハンドラーのフォーム属性のペアの形式はハンドラー リストに保存されます。
// events[type][handler.guid]
handlers[handler.guid] = handler; 7
/ / このイベントの使用法識別子をグローバルにキャッシュします
jQuery.event.global[type] = true;
});

elem = null; // IE のメモリ リークを防ぎます。
}、
guid: 1、
グローバル: {}、




jQuery.event.add は、jQuery.data を使用して、イベント名とイベントに関連する処理関数を有機的かつ規則的に結合し、jQuery.cache の要素に対応する空間に格納します。例を使用して追加プロセスを分析してみましょう: 次の jQuery(e1).bind("mouseover Mouseout", fn0);jQuery(e1).bind("mouseover Mouseout", fn1) ステートメントを受け取ったとします。

jQuery(e1).bind("mouseover Mouseout", fn0); の場合、②③はキャッシュから番号を取得できないので、先に初期化します。この時点のキャッシュ: {e1_uuid:{events:{},handle:fn}}。すると⑤でmouseover Mouseout名が初期化されます。この時点のキャッシュ: {e1_uuid:{events:{ Mouseover:{}, Mouseout:{}},handle:fn}}。 ⑥でブラウザイベントにハンドラー関数を登録します。次に、⑦でイベント名にハンドラー関数を追加します。この時点のキャッシュ: {e1_uuid:{events:{mouseover:{fn0_uuid:fn0},mouseout:{ fn0_uuid:fn0}},handle:fn}}。ここでは、プロキシを使用して関数の UUID を生成する役割を確認できます。

jQuery(e1).bind("mouseover Mouseout", fn1) の場合、②③は両方ともキャッシュからデータを取得します {e1_uuid:{events:{mouseover:{fn0_uuid:fn0},mouseout:{ fn0_uuid: fn0} } を実行し、⑤でmouseover:{fn0_uuid:fn0}、mouseout:{ fn0_uuid:fn0}の参照を取得します。次に、⑦でイベント名にハンドラー関数を登録します。この時点のキャッシュ: {e1_uuid:{events:{mouseover:{fn0_uuid:fn0, fn1_uuid:fn1,},mouseout:{ fn0_uuid:fn0, fn1_uuid:fn1}},handle:fn}}。

jQuery.event.add の非常に重要なタスクは、登録されたイベント関数を順序立てて保存することです。これにより、remove イベントと fire イベントの関数が見つかります。

//{elem_uuid_1:{events:{mouseover:{fn_uuid:fn1,fn_uuid1:fn2},
//mouseout:{fn_uuid:fn1,fn_uuid1:fn2}},handle:fn}}

6.2.2 トリガー



onclick などの登録されたイベント。ユーザーがこの要素をクリックすると、このイベントの登録済みイベント ハンドラーが自動的にトリガーされます。ただし、プログラムを使用してイベントのトリガーをシミュレートしたい場合は、イベントの強制トリガーを使用しなければならない場合があります。 IE では、.fireEvent() を使用してこれを実現できます。例:
ボタンが form.submit() を使用してフォームを送信する場合、必要に応じて、onsumbit イベントはアクティブにトリガーされません。 $(" :form")[0 ].fireEvent("onsubmit",)、これによりイベントがトリガーされます。

Mozilla には 3 つのステップがあります: var evt = document.createEvent('HTMLEvents');

evt.initEvent('change',true,true); ;

プロトタイプではこのように実装されています。そのため、jquery では実装が少し異なります。
コードをコピー コードは次のとおりです:

trigger: function(type, data, fn ) {
return this.each(function() {
jQuery.event.trigger(type, data, this, true, fn);
}) },

トリガーには3種類のパラメータがあり、データパラメータは登録されたイベント関数に実際の送信を提供します。 preventDefault が data[0] に存在する場合、data[0] はユーザー定義のラッピング イベントのスペースとして使用できます。 Fn は、イベントに対してすぐに使用できるイベント処理メソッドを提供できます。つまり、イベントを登録せずにハンドラー関数を渡すことでイベントを処理できます。 登録されている場合は、元のイベント処理関数の後に実行されます。

//このメソッドは、指定されたイベント タイプでバインドされたすべてのハンドラーをトリガーします。ただし、ブラウザのデフォルトのアクションは実行されません。
triggerHandler: function(type, data, fn) {
return this[0]&& jQuery.event.trigger(type,data,this[0],false, fn );
},



triggerHandle は、jQuery.event.trigger の donative パラメータを false に設定することで、ブラウザのデフォルトの処理メソッドの実行を防ぎます。トリガーとの違いは、jquery オブジェクトの最初の要素のみを処理することです。

上記の 2 つのメソッドはどちらも jQuery.event.trigger を呼び出してタスクを完了します。
コードをコピー コードは次のとおりです:

trigger: function(type, data, elem, donative, extra) {
data = jQuery.makeArray(data);//データは {xx:yy} にすることができます
// getData をサポートします!この形式では、exclusive = true は、登録された
// add イベントのすべての関数が名前空間の分類に従って実行されることを意味します。
if (type.indexOf("!") >= 0) { ①
type = type.slice(0, -1);var exclusive = true;
}
if (! elem) {// グローバル火災イベントを処理します。 ②
if (this.global[type])
jQuery.each(jQuery.cache, function() {
// 登録されているすべてのファイルをキャッシュから検索しますイベントのハンドラー関数をトリガーするイベントの要素
if (this.events && this.events[type])
jQuery.event.trigger(type, data, this.handle.elem); 🎜> } ; = jQuery.isFunction(elem[type] || null),
// データ パラメータがブラウザのイベント オブジェクトでない場合、イベント変数は true です。
// データ パラメータ自体の場合。が group の場合、最初の要素がブラウザのイベント オブジェクトでない場合は true です。
//イベントの場合は true。つまり、イベントが渡されない場合、偽のイベント オブジェクトが最初に構築され、data[0] に存在します。
event = !data[0] || !data[0].preventDefault;
// イベントオブジェクトを渡さずに偽のイベントオブジェクトを構築します。
if (event) {//配列に格納されている最初のもの④
data.unshift( { type : type, target : elem,
preventDefault : function() {},stopPropagation :
function() {}, timeStamp : now() });
data[0][expando] = true; // 偽のイベント オブジェクトを修正する必要はありません
}
data[0].type = type; //イベント名のエラーを防止します
//イベント登録関数の分類(名前空間)実行となります。全部ではありません。
if (exclusive) data[0].exclusive = true;

// プロトタイプなど従来の処理方法とは異なり、fireEventは使用しません
// Fireはブラウザに登録されます イベントハンドリングイベント内のメソッド。
// ここには 3 つの手順があります。まず、jQuery.event.add を通じて登録されたイベントを起動します。このイベント
// カスタム イベント (ブラウザー イベントには登録されていない) である可能性があります。
// 2 番目のステップは、elem.onclick メソッドによって fire によって登録されたイベントのローカル処理関数です。
// 3 番目のステップは、fire のデフォルトのイベント処理メソッドです (ローカルの onclick メソッドによって登録されます
/ / 存在しません)。
// jQuery.event.add で登録したトリガーイベントです,
var handle = jQuery.data(elem, "handle"); ⑤
if (handle)val = handle.apply( elem , data); //ここでのデータは複数のパラメータに分かれています
//elem.onfoo=function(),
などのローカル処理メソッドを登録することで処理がトリガーされます//ただし、リンクの .click( ) トリガーされないため、addEvent
// メソッドを通じて登録されたイベント処理メソッドは実行されません。
if ((!fn || (jQuery.nodeName(elem, 'a') && type == "click")) ⑥
&& elem["on" type]&& elem["on" type] .apply(elem,data) === false)
val = false
//最初のいくつかの追加関数パラメータはデータを通じて与えられます。偽造されたイベントはここで削除されます。
//最後のパラメータは、一連のイベント処理関数によって返される結果であり、通常はブール値です。
//この関数は、この結果に基づいて仕上げ作業を処理できます。
if (event) data.shift();
// extra で与えられた関数の処理をトリガーします。
if (extra && jQuery.isFunction(extra)) { ⑦
ret = extra.apply(elem, val == null ? data : data.concat(val));戻り値がある場合、トリガーの戻り値はその戻り値
// ない場合、一連のイベント処理関数の最後の戻り値。一般的に bool
if (ret !== unknown) val = ret;
}
// .onclick などの登録されたイベントがない場合に、デフォルトのローカル イベント メソッドをトリガーします。前述の内容を追加します。 実行イベント ハンドラー関数の戻り値が false の場合にのみ実行されます。
//寄付を介して実行するかどうかを制御することもできます。
//たとえば、フォーム内で this.submit() を使用してフォームを送信できます。
if (fn && donative !== false && val !== false ⑧
&& !(jQuery) .nodeName(elem, 'a') && type == "click")) {
this.triggered = true;
try {elem[type](); //一部の非表示要素については、IE がレポートします。エラー
} catch (e) {}
}
this.triggered = false;
}
return val;




Jqueryのfireイベントのメソッドはプロトタイプでの実装とは全く異なります。 Ext と YUI には、イベントを強制的にトリガーする方法がありません。一般的に考えると、ブラウザ イベントをトリガーするプログラムは、fireEvent メソッドまたはdispatchEvent メソッドを使用して実行する必要があります。

しかし、jquery は異なるアプローチをとります。 jquery.event.add を通じて登録されたイベント (カスタマイズされたか、ブラウザ イベントに登録されたかに関係なく) は、要素とイベント名に対応するキャッシュに保存されます。ブラウザのトリガーでは、これは効果がありません。ただし、プログラムを待って強制的にトリガーされた場合に、対応するイベント処理関数をキャッシュから取得するためです。このとき、ブラウザのイベントは破棄されます。一部のカスタム イベント関数もここで実行できます。 ⑤など。

click や elem.onclick=function(){} などの HTML タグを通じて登録されたイベント関数の場合。 ⑥では、onclick形式のコールバック関数を使用して要素を実行するだけです。この dom0 メソッドで登録できる関数は 1 つだけです。

onclick などのイベント ハンドラーがない場合、ブラウザはデフォルトのハンドラーを実行することがあります。 form.submit() など。 ⑧から、このようなデフォルトのイベント処理もパラメーター donative を通じて制御できることがわかります。

プログラムはイベントを手動でトリガーします。問題の 1 つは、イベントがどのように生成されるかです。つまり、イベントを生成して関数に渡すブラウザーがありません。プロトタイプは、新しく生成された dataavailable イベントを使用します。このようなイベントはほとんど効果がありません。また、Jquery は、④ のように、fake を使用してイベントを 1 つずつ偽造します。プロトタイプ イベントと比べて、トリガー関数のパラメーターを通じて必要なイベントを渡すことができるという利点があります。プロトタイプはできません。

上記の分析により、Jquery はブラウザのトリガー イベントの実行プロセスをシミュレートすることによってこのトリガー関数を構築していることがわかります。最初に dom1 モードで登録されたイベント (addEvent) を実行し、次に dom0 モードで登録されたイベントを実行し、最後にデフォルトのイベント処理を実行するかどうかを確認します。

⑦では、トリガーがコールバック関数とパラメーターを渡すことによって、実行されたイベント処理関数の結果の判定と処理を完了し、新しい結果を形成してそれをメソッドを通じて返す可能性があることがわかります。トリガー機能。これは時々便利です。



これらに加えて、イベント処理関数 (名前空間) を分類することもでき、適切なタイミングで異なるカテゴリのイベント処理関数を呼び出すことができます (登録する jquery.event.add 経由)。 )。この分類の処理はハンドルに実装されています。
コードをコピー コードは次のとおりです:

handle: function(event) {
// 未定義または false を返します
var val, ret, namespace, all, handlers;
// 渡されたパラメータを変更しました。ここがリファレンスです。
Event = argument[0] = jQuery.event.fix(event || window.event);
// 名前空間処理
namespace =event.type.split("."); Event.type = namespace[0];
Namespace = namespace[1]
// all = true はハンドラー、名前空間が存在せず、
//event.exclusive が存在しないことを示します。 is false の場合、all=true。
All = !namespace && !event.exclusive;
// 要素のイベントにキャッシュされたイベント名のハンドラー リストを検索します。
handlers = (jQuery.data(this, " events") || {})[event.type];
for (var j in handlers) { // 各ハンドラー関数が実行されます
var handler = handlers[j]; // フィルタークラスによる関数
if (all || handler.type == namespace) {
// 後で削除するために参照を渡します
event.handler = handler;
event.data = handler.data;//追加時に追加する ret = handler.apply(this, argument);//イベント処理関数を実行
if (val !== false)
val = ret;// 1 つの処理関数が false を返す限り、この関数は false を返します。
if (ret === false) {//ブラウザのデフォルト アクション
event.preventDefault(); を実行しません。 stopPropagation ();
}
}
}
return val; }


関連ラベル:
ソース:php.cn
このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。
最新の問題
人気のチュートリアル
詳細>
最新のダウンロード
詳細>
ウェブエフェクト
公式サイト
サイト素材
フロントエンドテンプレート