Maison > interface Web > js tutoriel > Gestion par JavaScript des problèmes de compatibilité avec IE8

Gestion par JavaScript des problèmes de compatibilité avec IE8

php中世界最好的语言
Libérer: 2018-03-16 14:28:04
original
7888 Les gens l'ont consulté

这次给大家带来JavaScript关于IE8兼容问题的处理,JavaScript关于IE8兼容问题处理的注意事项有哪些,下面就是实战案例,一起来看一下。

最初对做兼容性的认知只停留在UI层面,但其实UI层面都还好,因为毕竟你可以直接看得见现象,更为重要的是在JavaScript层面,因为这个部分涉及到功能性,前者最多是体验性的问题。下面扯一下这几天遇到的IE8相关的兼容性问题。

1.所有$.ajax失效

刚开始看到的现象是IE8/9页面切到了留言页面,没有进入正常流程。打开fiddler都没抓到请求,奇怪了,打出$.ajax error方法中的返回值。xhr.status的值都是0,原来请求没有发出去。心想:怎么没有发出去呢,难道是跨域问题,不对啊,服务端已经允许跨域了。后来一查(IE8/9下的跨域资源请求),原来请求被IE8/9阻止了,所以服务端允许跨域了也没有用。当时网上有两种办法,1个是设置浏览器,允许通过域访问数据源。2个是加入一个js插件,也就是让IE8/9/10走IE自己支持的XDomainRequest发送请求。第一个方法只能玩玩,不可能让用户去操作套路那么深的步骤。第二个方法github上有两套js。

https://github.com/MoonScript/jQuery-ajaxTransport-XDomainRequest

https://github.com/jaubourg/ajaxHooks/blob/master/src/xdr.js

引用后都不用改代码,刚开始还有点小激动。

<!--[if (IE 8)|(IE 9)]><script src="Js/jQuery.XDOmainRequest.js"></script><![endif]-->
Copier après la connexion

加上去一看,请求发过去了,界面进入了正常流程。but,又发现返回的值不对,说与服务端,服务端说你的值没传过来,怎么可能,代码都没动,原来这个XDomainRequest是参数走的header,先天性缺少contentType,没有参数的请求返回是正确的,有参数的后台就没有读到。又寻思着难道要后台改代码,支持一下这种方式?我迟疑了。然后就到群里和队友们讨论下。大家给出的方案就是,用Nginx配置个代理就好了。啊,一时激动的有点难以言表。绕了一大圈,啥都不用改,服务器上配置下就好了。因为,既然是跨域问题,最根本的方法就是让它不用跨域了。这种前后端分离的架构,网站(html)和api都是不同的域名.

location /api/-Real--Forwarded-//./api/;
 }
Copier après la connexion

所以这才是最佳解决方案。当时就想,自己对跨域的认知不足,遇到问题都是横向的找办法,没有往上层想。还是有给力的队友。这个问题stackoverflow也有讨论:

http://stackoverflow.com/questions/10232017/ie9-jquery-ajax-with-cors-returns-access-is-denied

http://stackoverflow.com/questions/3362474/jquery-ajax-fails-in-ie-on-cross-domain-calls

2.消息怎么变长了

这个问题的现象看起来很神奇,用户在IE8上发送长消息(比如1000字)开始能显示在对话框中,但是一刷新就没了。别的浏览器刷新后长消息都还在。我打开日志,ajax是进入了成功回调,但是返回值有文字提示:'消息过长被阻止'。也就是说我看成功回调了就立马显示到界面上了,但是并没有成功,所以刷新之后拿不到那条消息。ajax的succes只是代表请求成功,并不代表调用api对了。这是我犯得低级错误,没有和api同事确认。但为什么消息会变长呢,一开始把字数调500,ie8能够发了。说明请求确实变长了。打开日志发现。汉字都被转码了。比如:

    var data={name:"博客园"}
     alert(JSON.stringify(data));
Copier après la connexion

IE8会得到

也就是说本质上是json转义的问题。之前给ie8加了一个json2.

<!--[if IE 8]><script src="json2.min.js"></script><![endif]-->
Copier après la connexion

看来还不行。换成json3,1000字也正常了。

<!--[if IE 8]><script src="json3.min.js"></script><![endif]-->
Copier après la connexion

3.flash检测

如果你想准确的判断用户的IE到底有没flash。得用SWFObject对象

/*!    SWFObject v2.3.20130521 <http://github.com/swfobject/swfobject>
    is released under the MIT License <http://www.opensource.org/licenses/mit-license.php>*/var swfobject = function () {    var D = "undefined", r = "object", T = "Shockwave Flash", Z = "ShockwaveFlash.ShockwaveFlash", q = "application/x-shockwave-flash", S = "SWFObjectExprInst", x = "onreadystatechange", Q = window, h = document, t = navigator, V = false, X = [], o = [], P = [], K = [], I, p, E, B, L = false, a = false, m, G, j = true, l = false, O = function () { var ad = typeof h.getElementById != D && typeof h.getElementsByTagName != D && typeof h.createElement != D, ak = t.userAgent.toLowerCase(), ab = t.platform.toLowerCase(), ah = ab ? /win/.test(ab) : /win/.test(ak), af = ab ? /mac/.test(ab) : /mac/.test(ak), ai = /webkit/.test(ak) ? parseFloat(ak.replace(/^.*webkit\/(\d+(\.\d+)?).*$/, "$1")) : false, aa = t.appName === "Microsoft Internet Explorer", aj = [0, 0, 0], ae = null; if (typeof t.plugins != D && typeof t.plugins[T] == r) { ae = t.plugins[T].description; if (ae && (typeof t.mimeTypes != D && t.mimeTypes[q] && t.mimeTypes[q].enabledPlugin)) { V = true; aa = false; ae = ae.replace(/^.*\s+(\S+\s+\S+$)/, "$1"); aj[0] = n(ae.replace(/^(.*)\..*$/, "$1")); aj[1] = n(ae.replace(/^.*\.(.*)\s.*$/, "$1")); aj[2] = /[a-zA-Z]/.test(ae) ? n(ae.replace(/^.*[a-zA-Z]+(.*)$/, "$1")) : 0 } } else { if (typeof Q.ActiveXObject != D) { try { var ag = new ActiveXObject(Z); if (ag) { ae = ag.GetVariable("$version"); if (ae) { aa = true; ae = ae.split(" ")[1].split(","); aj = [n(ae[0]), n(ae[1]), n(ae[2])] } } } catch (ac) { } } } return { w3: ad, pv: aj, wk: ai, ie: aa, win: ah, mac: af } }(), i = function () { if (!O.w3) { return } if ((typeof h.readyState != D && (h.readyState === "complete" || h.readyState === "interactive")) || (typeof h.readyState == D && (h.getElementsByTagName("body")[0] || h.body))) { f() } if (!L) { if (typeof h.addEventListener != D) { h.addEventListener("DOMContentLoaded", f, false) } if (O.ie) { h.attachEvent(x, function aa() { if (h.readyState == "complete") { h.detachEvent(x, aa); f() } }); if (Q == top) { (function ac() { if (L) { return } try { h.documentElement.doScroll("left") } catch (ad) { setTimeout(ac, 0); return } f() }()) } } if (O.wk) { (function ab() { if (L) { return } if (!/loaded|complete/.test(h.readyState)) { setTimeout(ab, 0); return } f() }()) } } }(); function f() { if (L || !document.getElementsByTagName("body")[0]) { return } try { var ac, ad = C("span"); ad.style.display = "none"; ac = h.getElementsByTagName("body")[0].appendChild(ad); ac.parentNode.removeChild(ac); ac = null; ad = null } catch (ae) { return } L = true; var aa = X.length; for (var ab = 0; ab < aa; ab++) { X[ab]() } } function M(aa) { if (L) { aa() } else { X[X.length] = aa } } function s(ab) { if (typeof Q.addEventListener != D) { Q.addEventListener("load", ab, false) } else { if (typeof h.addEventListener != D) { h.addEventListener("load", ab, false) } else { if (typeof Q.attachEvent != D) { g(Q, "onload", ab) } else { if (typeof Q.onload == "function") { var aa = Q.onload; Q.onload = function () { aa(); ab() } } else { Q.onload = ab } } } } } function Y() { var aa = h.getElementsByTagName("body")[0]; var ae = C(r); ae.setAttribute("style", "visibility: hidden;"); ae.setAttribute("type", q); var ad = aa.appendChild(ae); if (ad) { var ac = 0; (function ab() { if (typeof ad.GetVariable != D) { try { var ag = ad.GetVariable("$version"); if (ag) { ag = ag.split(" ")[1].split(","); O.pv = [n(ag[0]), n(ag[1]), n(ag[2])] } } catch (af) { O.pv = [8, 0, 0] } } else { if (ac < 10) { ac++; setTimeout(ab, 10); return } } aa.removeChild(ae); ad = null; H() }()) } else { H() } } function H() { var aj = o.length; if (aj > 0) { for (var ai = 0; ai < aj; ai++) { var ab = o[ai].id; var ae = o[ai].callbackFn; var ad = { success: false, id: ab }; if (O.pv[0] > 0) { var ah = c(ab); if (ah) { if (F(o[ai].swfVersion) && !(O.wk && O.wk < 312)) { w(ab, true); if (ae) { ad.success = true; ad.ref = z(ab); ad.id = ab; ae(ad) } } else { if (o[ai].expressInstall && A()) { var al = {}; al.data = o[ai].expressInstall; al.width = ah.getAttribute("width") || "0"; al.height = ah.getAttribute("height") || "0"; if (ah.getAttribute("class")) { al.styleclass = ah.getAttribute("class") } if (ah.getAttribute("align")) { al.align = ah.getAttribute("align") } var ak = {}; var aa = ah.getElementsByTagName("param"); var af = aa.length; for (var ag = 0; ag < af; ag++) { if (aa[ag].getAttribute("name").toLowerCase() != "movie") { ak[aa[ag].getAttribute("name")] = aa[ag].getAttribute("value") } } R(al, ak, ab, ae) } else { b(ah); if (ae) { ae(ad) } } } } } else { w(ab, true); if (ae) { var ac = z(ab); if (ac && typeof ac.SetVariable != D) { ad.success = true; ad.ref = ac; ad.id = ac.id } ae(ad) } } } } } X[0] = function () { if (V) { Y() } else { H() } }; function z(ac) { var aa = null, ab = c(ac); if (ab && ab.nodeName.toUpperCase() === "OBJECT") { if (typeof ab.SetVariable !== D) { aa = ab } else { aa = ab.getElementsByTagName(r)[0] || ab } } return aa } function A() { return !a && F("6.0.65") && (O.win || O.mac) && !(O.wk && O.wk < 312) } function R(ad, ae, aa, ac) {        var ah = c(aa); aa = W(aa); a = true; E = ac || null; B = { success: false, id: aa }; if (ah) {            if (ah.nodeName.toUpperCase() == "OBJECT") { I = J(ah); p = null } else { I = ah; p = aa } ad.id = S; if (typeof ad.width == D || (!/%$/.test(ad.width) && n(ad.width) < 310)) { ad.width = "310" } if (typeof ad.height == D || (!/%$/.test(ad.height) && n(ad.height) < 137)) { ad.height = "137" } var ag = O.ie ? "ActiveX" : "PlugIn", af = "MMredirectURL=" + encodeURIComponent(Q.location.toString().replace(/&/g, "%26")) + "&MMplayerType=" + ag + "&MMdoctitle=" + encodeURIComponent(h.title.slice(0, 47) + " - Flash Player Installation"); if (typeof ae.flashvars != D) { ae.flashvars += "&" + af } else { ae.flashvars = af } if (O.ie && ah.readyState != 4) {                var ab = C("p");
                aa += "SWFObjectNew"; ab.setAttribute("id", aa); ah.parentNode.insertBefore(ab, ah); ah.style.display = "none"; y(ah)
            } u(ad, ae, aa)
        }
    } function b(ab) { if (O.ie && ab.readyState != 4) { ab.style.display = "none"; var aa = C("p"); ab.parentNode.insertBefore(aa, ab); aa.parentNode.replaceChild(J(ab), aa); y(ab) } else { ab.parentNode.replaceChild(J(ab), ab) } } function J(af) { var ae = C("p"); if (O.win && O.ie) { ae.innerHTML = af.innerHTML } else { var ab = af.getElementsByTagName(r)[0]; if (ab) { var ag = ab.childNodes; if (ag) { var aa = ag.length; for (var ad = 0; ad < aa; ad++) { if (!(ag[ad].nodeType == 1 && ag[ad].nodeName == "PARAM") && !(ag[ad].nodeType == 8)) { ae.appendChild(ag[ad].cloneNode(true)) } } } } } return ae } function k(aa, ab) { var ac = C("p"); ac.innerHTML = "<object classid=&#39;clsid:D27CDB6E-AE6D-11cf-96B8-444553540000&#39;><param name=&#39;movie&#39; value=&#39;" + aa + "&#39;>" + ab + "</object>"; return ac.firstChild } function u(ai, ag, ab) { var aa, ad = c(ab); ab = W(ab); if (O.wk && O.wk < 312) { return aa } if (ad) { var ac = (O.ie) ? C("p") : C(r), af, ah, ae; if (typeof ai.id == D) { ai.id = ab } for (ae in ag) { if (ag.hasOwnProperty(ae) && ae.toLowerCase() !== "movie") { e(ac, ae, ag[ae]) } } if (O.ie) { ac = k(ai.data, ac.innerHTML) } for (af in ai) { if (ai.hasOwnProperty(af)) { ah = af.toLowerCase(); if (ah === "styleclass") { ac.setAttribute("class", ai[af]) } else { if (ah !== "classid" && ah !== "data") { ac.setAttribute(af, ai[af]) } } } } if (O.ie) { P[P.length] = ai.id } else { ac.setAttribute("type", q); ac.setAttribute("data", ai.data) } ad.parentNode.replaceChild(ac, ad); aa = ac } return aa } function e(ac, aa, ab) { var ad = C("param"); ad.setAttribute("name", aa); ad.setAttribute("value", ab); ac.appendChild(ad) } function y(ac) { var ab = c(ac); if (ab && ab.nodeName.toUpperCase() == "OBJECT") { if (O.ie) { ab.style.display = "none"; (function aa() { if (ab.readyState == 4) { for (var ad in ab) { if (typeof ab[ad] == "function") { ab[ad] = null } } ab.parentNode.removeChild(ab) } else { setTimeout(aa, 10) } }()) } else { ab.parentNode.removeChild(ab) } } } function U(aa) { return (aa && aa.nodeType && aa.nodeType === 1) } function W(aa) { return (U(aa)) ? aa.id : aa } function c(ac) { if (U(ac)) { return ac } var aa = null; try { aa = h.getElementById(ac) } catch (ab) { } return aa } function C(aa) { return h.createElement(aa) } function n(aa) { return parseInt(aa, 10) } function g(ac, aa, ab) { ac.attachEvent(aa, ab); K[K.length] = [ac, aa, ab] } function F(ac) { ac += ""; var ab = O.pv, aa = ac.split("."); aa[0] = n(aa[0]); aa[1] = n(aa[1]) || 0; aa[2] = n(aa[2]) || 0; return (ab[0] > aa[0] || (ab[0] == aa[0] && ab[1] > aa[1]) || (ab[0] == aa[0] && ab[1] == aa[1] && ab[2] >= aa[2])) ? true : false } function v(af, ab, ag, ae) { var ad = h.getElementsByTagName("head")[0]; if (!ad) { return } var aa = (typeof ag == "string") ? ag : "screen"; if (ae) { m = null; G = null } if (!m || G != aa) { var ac = C("style"); ac.setAttribute("type", "text/css"); ac.setAttribute("media", aa); m = ad.appendChild(ac); if (O.ie && typeof h.styleSheets != D && h.styleSheets.length > 0) { m = h.styleSheets[h.styleSheets.length - 1] } G = aa } if (m) { if (typeof m.addRule != D) { m.addRule(af, ab) } else { if (typeof h.createTextNode != D) { m.appendChild(h.createTextNode(af + " {" + ab + "}")) } } } } function w(ad, aa) { if (!j) { return } var ab = aa ? "visible" : "hidden", ac = c(ad); if (L && ac) { ac.style.visibility = ab } else { if (typeof ad === "string") { v("#" + ad, "visibility:" + ab) } } } function N(ab) { var ac = /[\\\"<>\.;]/; var aa = ac.exec(ab) != null; return aa && typeof encodeURIComponent != D ? encodeURIComponent(ab) : ab } var d = function () { if (O.ie) { window.attachEvent("onunload", function () { var af = K.length; for (var ae = 0; ae < af; ae++) { K[ae][0].detachEvent(K[ae][1], K[ae][2]) } var ac = P.length; for (var ad = 0; ad < ac; ad++) { y(P[ad]) } for (var ab in O) { O[ab] = null } O = null; for (var aa in swfobject) { swfobject[aa] = null } swfobject = null }) } }(); return {
        registerObject: function (ae, aa, ad, ac) { if (O.w3 && ae && aa) { var ab = {}; ab.id = ae; ab.swfVersion = aa; ab.expressInstall = ad; ab.callbackFn = ac; o[o.length] = ab; w(ae, false) } else { if (ac) { ac({ success: false, id: ae }) } } }, getObjectById: function (aa) { if (O.w3) { return z(aa) } }, embedSWF: function (af, al, ai, ak, ab, ae, ad, ah, aj, ag) { var ac = W(al), aa = { success: false, id: ac }; if (O.w3 && !(O.wk && O.wk < 312) && af && al && ai && ak && ab) { w(ac, false); M(function () { ai += ""; ak += ""; var an = {}; if (aj && typeof aj === r) { for (var aq in aj) { an[aq] = aj[aq] } } an.data = af; an.width = ai; an.height = ak; var ar = {}; if (ah && typeof ah === r) { for (var ao in ah) { ar[ao] = ah[ao] } } if (ad && typeof ad === r) { for (var am in ad) { if (ad.hasOwnProperty(am)) { var ap = (l) ? encodeURIComponent(am) : am, at = (l) ? encodeURIComponent(ad[am]) : ad[am]; if (typeof ar.flashvars != D) { ar.flashvars += "&" + ap + "=" + at } else { ar.flashvars = ap + "=" + at } } } } if (F(ab)) { var au = u(an, ar, al); if (an.id == ac) { w(ac, true) } aa.success = true; aa.ref = au; aa.id = au.id } else { if (ae && A()) { an.data = ae; R(an, ar, al, ag); return } else { w(ac, true) } } if (ag) { ag(aa) } }) } else { if (ag) { ag(aa) } } }, switchOffAutoHideShow: function () { j = false }, enableUriEncoding: function (aa) { l = (typeof aa === D) ? true : aa }, ua: O, getFlashPlayerVersion: function () { return { major: O.pv[0], minor: O.pv[1], release: O.pv[2] } }, hasFlashPlayerVersion: F, createSWF: function (ac, ab, aa) { if (O.w3) { return u(ac, ab, aa) } else { return undefined } }, showExpressInstall: function (ac, ad, aa, ab) { if (O.w3 && A()) { R(ac, ad, aa, ab) } }, removeSWF: function (aa) { if (O.w3) { y(aa) } }, createCSS: function (ad, ac, ab, aa) { if (O.w3) { v(ad, ac, ab, aa) } }, addDomLoadEvent: M, addLoadEvent: s, getQueryParamValue: function (ad) {            var ac = h.location.search || h.location.hash;            if (ac) { if (/\?/.test(ac)) { ac = ac.split("?")[1] } if (ad == null) { return N(ac) } var ab = ac.split("&"); for (var aa = 0; aa < ab.length; aa++) { if (ab[aa].substring(0, ab[aa].indexOf("=")) == ad) { return N(ab[aa].substring((ab[aa].indexOf("=") + 1))) } } } return ""
        }, expressInstallCallback: function () { if (a) { var aa = c(S); if (aa && I) { aa.parentNode.replaceChild(I, aa); if (p) { w(p, true); if (O.ie) { I.style.display = "block" } } if (E) { E(B) } } a = false } }, version: "2.3"
    }
}();
Copier après la connexion

View Code

 function hasFlash() {        return swfobject.hasFlashPlayerVersion("1");
    }
Copier après la connexion

通过这方法判断是最准确的。

4.图片显示问题

1)在https的网站上出现http的请求,浏览器都会给出安全提醒,就ie喜欢弹出来。

这个也还好,微信在ie8中打开也会有这样的提示,除非文件服务器也是https的,不然前端也是没解。如果是在https中请求http的ajax,那直接挂掉。

2)IE8上在界面对大图片设置max-width,会出现把父元素撑得很大而图片显示在一边,留下一大块空白。这个解决办法就是给父类也加个max-width的样式限制一下。

5.socket.io遇到https

这是这几天一个严重的问题,到https上,ie8/9都收不到消息。我为之很苦恼,因为socket都显示连接成功了,也订阅消息了,就是没收到消息。这个socket.io号称都支持到ie6的。各种搜索都没有看到过相关问题。我有个经验,如果一个问题,你搜了很都没有找到类似的问题,可能有两点,要么这是个很傻的问题,哪个地方设置下就好了,要么就是个很偏很冷门的问题。但是明显前者的可能性更大。

  socket = io(&#39;/&#39;, { &#39;transports&#39;: [&#39;websocket&#39;, &#39;polling&#39;] });
  socket.on("connect", function () {  console.log(&#39;连接成功&#39;)                                    
  }); //监听频道
 socket.on(&#39;messages&#39;, function (res) {
 console.log("收到消息",res);
 });
Copier après la connexion

其实在http上的测试环境ie8是ok的,有一天晚上突然调通了,我还高兴的备份了代码回家睡了个好觉,第二天一来打开一看,懵逼了,还是不行。难道是幻觉,昨天还在群里和队友说好了,真是啪啪啪的打脸。然后再也没通过了。其实socket.io的调用代码很简单,就那么几句。我再怎么摆弄也没啥用。最后还是没办法,在ie8/9上面切到了传统的轮询。这个问题最后还是没有解决,同事说可能是F5或者https的安全协议引起的。

6.不支持数组的indexof

如果是现代浏览器我们可以通过indexOf来判断一个元素是否存在于数组

if (!~loadmesg.indexOf(json.MsgID))
Copier après la connexion

但ie8不支持。得改成for循环。

7.隐藏元素初始化上传插件失败

上传的时候使用的是plupload插件,但如果目标元素是隐藏的,ie8上初始化会无效。

8.IE标准模式失效

我们可以用mate来指定ie8的渲染方式:

  <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
Copier après la connexion

但我发现无论我怎么加,都是以ie7的方式打开的。然后就重新做了简单的也没,只有meta和文字,发现是有效果的,也就是说页面上的某些原因导致ie8又自己切换到了ie7的兼容模式

然后拿掉就可以了。之前这么写是为了添加一个过渡的样式。没想到造成了这种影响。只有再换一种方式实现。

9.jquery的一种写法不支持

 var $a = $('<a target="_blank" class="&#39; + c + &#39;"     href=&#39; + rightData[i].CustomerRightNavLink + &#39;></a>');            var img = '<img src=&#39; + rightData[i].CustomerRightNavImage + &#39; />';
            $a.html(img);
            $(".hold").append($a);
Copier après la connexion

引用的是jquery1.11.3 在ie8中会报错。修改如下:

 var str = '<a target="_blank" class="&#39; + c + &#39;"     href=&#39; + rightData[i].CustomerRightNavLink + &#39;><img src=&#39; + rightData[i].CustomerRightNavImage + &#39; /></a>'
            $(".hold").append(str);
Copier après la connexion

小结:做兼容性也是为了体验性和稳定性,每一个异常现象背后都是对一些基础知识的考验,以前做网站基本上都是一个人从头做到位,跨域问题几乎都没遇到过,兼容性上也没有死磕。现在正经做前端几个月了,感觉做兼容性是每个前端的必备课,后端都知道现代浏览器方便好用,但是你写代码是为了服务用户的,不是方便自己,再者,做兼容性确实考验各方面的知识,最好前端还是要懂后端的以及网络方面的知识。这样遇到问题的时候不至于茫然无措,因为前端看到的现象已经是异常的最后面目,你要分析找出最根本的原因,然后找对应的同事帮忙解决

相信看了本文案例你已经掌握了方法,更多精彩请关注php中文网其它相关文章!

推荐阅读:

微信分享功能的开发

公众号支付接口的开发

H5的缓存Manifest的使用

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Étiquettes associées:
source:php.cn
Déclaration de ce site Web
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn
Tutoriels populaires
Plus>
Derniers téléchargements
Plus>
effets Web
Code source du site Web
Matériel du site Web
Modèle frontal