整合營銷服務商

          電腦端+手機端+微信端=數據同步管理

          免費咨詢熱線:

          第69節 跨域請求-Web前端開發之JavaScript-零點程序員-王唯

          域請求:

          現代的所有的瀏覽器都遵守同源策略,所謂的源指的就是一個域名、一個服務器,同源是指兩個腳本擁有相同的域名,不同源指的是不同的域名,同源策略指的是,一個源的腳本不能讀取或操作其他源的http響應和cookie,也就是出于安全方面的考慮,頁面中的JavaScript無法訪問其他服務器上的數據,這就是所謂的“同源策略”。
          同源是指協議、域名和端口都一致。

          不同源限制的內容:

          • Cookie、LocalStorge、IndexedDb等存儲性內容;
          • DOM節點;
          • Ajax請求;

          跨域請求時,不同域的服務器是返回了數據的,只不過瀏覽器攔截了響應數據;同時也說明了跨域并不能完全阻止CSRF,因為請求畢竟是發出去了;

          CORS(Cross-Origin Response Sharing)跨域資源共享:

          通過XHR實現Ajax通信的主要限制,是跨域安全策略;默認情況下,只能訪問同一個域中的資源,這種安全策略可以預防某些惡意行為,如:

          var xhr = new XMLHttpRequest();
          xhr.onload = function(){
          console.log(xhr.responseText);
          }
          xhr.open("GET", "https://www.zeronetwork.cn/study/index.html");
          xhr.send(null);

          其拋出了CORS policy異常;

          XHR2規范了在通過HTTP響應中如何選擇合適的CORS(Cross-Origin Response Sharing,跨域資源共享)去跨域訪問資源;其定義了在必須訪問跨源資源時,瀏覽器與服務器應該如何溝通;CORS的基本思想是,就是使用自定義的HTTP頭部讓瀏覽器與服務器進行溝通,從面決定請求或響應是否應該成功;

          比如一個簡單的使用GET或POST發送的請求,默認情況下它沒有自定義的頭,但一般會包括一個Origin請求頭,其中包含請求頁面的源信息(協議、域名和端口),以便服務器根據這個頭部信息來決定是否給予響應,如Origin頭部示例:

          Origin: https://www.zeronetwork.cn

          如果服務器認為這個請求可以接受,就在響應的Access-Control-Allow-Origin([??la?])頭中回發相同的源信息(如果是公共資源,可以回發”*”),例如:

          Access-Control-Allow-Origin: https://www.zeronetwork.cn

          如果沒有這個響應頭,或者有這個響應頭但與請求Origin頭信息不匹配,瀏覽器就會駁回請求;反之,瀏覽器會處理請求;

          實現跨域:

          IE和標準瀏覽器已經實現了各自的跨域解決方案;

          標準瀏覽器對CORS的實現:

          在標準瀏覽器中,客戶端在使用Ajax跨域請求時,拋出異常,不能訪問;如:

          var xhr = new XMLHttpRequest();
          xhr.onreadystatechange = function(){
          if (xhr.readyState == 4 && xhr.status == 200) {
          console.log(xhr.responseText);
          }
          }
          xhr.open("GET","https://www.b.com/normal/example.json");
          xhr.send(null);

          被請求的服務端需要設置Access-Control-Allow-Origin響應頭,以便于瀏覽器識別它是否為可信源。

          例如,在Apache服務器中,在服務器的配置中添加如下設置:

          Header set Access-Control-Allow-Origin 'origin-list'

          對于Nginx,設置此http頭的命令是:

          add_header 'Access-Control-Allow-Origin' 'origin-list'

          或者使用.htaccess文件配置,如:

          <IfModule mod_headers.c>
          Header set Access-Control-Allow-Origin "*"
          </IfModule>
          order allow,deny
          allow from all

          應用:

          xhr.open("GET","https://www.b.com/cors/example.json");

          單獨為某個后端程序設置響應頭,例如b.com/cors.php:

          <?php
          header("Access-Control-Allow-Origin: *");
          echo "跨域訪問b.com/cors.php";

          無論同源請求還是跨源請求都使用相同的接口,因此對地本地資源,最好使用相對URL,在訪問遠程資源時再使用絕對URL;這樣做能消除歧義,避免出現限制訪問頭部或本地cookie信息等問題。

          IE對CORS的實現:

          微軟在IE8中引入了XDR(XDomainRequest)對象,其與XHR類似,其可直接用于發起安全的跨域請求,實現安全可靠的跨域通信;

          var xdr = new XDomainRequest();
          console.log(xdr);

          IE11和標準瀏覽器并不支持;

          XDR對象的使用方法與XHR對象非常相似,兩者擁有幾乎相同的屬性和方法,也是調用open()方法,再調用send()方法;但與XHR對象的open()方法不同,XDR對象的open()方法只接收兩個參數:請求的類型和URL;如:

          var xdr = new XDomainRequest();
          console.log(xdr);
          // xdr.open("GET", "http://www.c.com/nocors.php");
          xdr.open("GET", "example.php");
          xdr.onload = function(){
          console.log(xdr.responseText);
          }
          xdr.send();

          此時,不管是跨域的還是同源的都不允許訪問,拋出“在 Access-Control-Allow-Origin 標頭中未找到源”;

          XDR對象的安全機制中部分實現了CORS,后端也需要設置 Access-Control-Allow-Origin響應頭,如c.com/cors.php:

          <?php
          header("Access-Control-Allow-Origin: *");
          echo "設置了ACAO響應頭";

          請求端:

          xdr.open("GET", "http://www.c.com/cors.php");

          XDR對象屬性和事件:

          請求返回后,會觸發onload事件,響應的數據也會保存在responseText屬性中,響應的MIME類型保存在contentType屬性中,如:

          var xdr = new XDomainRequest();
          xdr.onload = function(){
          console.log(xdr.contentType); // application/json
          console.log(xdr.responseText);
          }
          xdr.open("post","http://www.c.com/cors/example.json");
          xdr.send(null);

          例如再請求一個同源的contentType.php:

          header("Access-Control-Allow-Origin: *");
          header("Content-Type: application/json");
          echo '{"username":"王唯","age":18,"sex":true}';

          在接收到響應后,只能訪問響應的原始文本,不能確定響應的狀態代碼(也就是它沒有status屬性);而且,只要響應有效就會觸發onload事件,如果失敗就會觸發error事件,但除了錯誤本身之外,沒有其他信息可以確定請求是否成功,所以唯一能夠確定的就只有請求未成功;

          要檢測錯誤,如要指定error事件處理程序,如:

          xdr.onerror = function(){
          console.log("出現錯誤");
          }

          由于導致XDR請求失敗的因素很多,因此,最好通過error事件處理程序來捕獲該事件,否則,即使請求失敗也不會有任何提示。

          在請求返回前調用abort()方法可以終止請求,如:

          xdr.abort();

          與XHR對象一樣,XDR也支持timout屬性和ontimeout事件,如:

          xdr.timeout=1000;
          xdr.ontimeout = function(){
          console.log("請求超過1秒");
          };

          onprogress事件:

          應該始終定義 xdr.onprogress 事件,即使它是一個空函數,否則 XDomainRequest 對于重復請求,可能不會觸發 onload 事件;

          xdr.onprogress = function(event){
          console.log(event);
          };

          XDR與XHR的不同之外:

          1、必須使用 HTTP 或 HTTPS 協議訪問目標 URL:
          因為XDR對象依賴于一個HTTP響應頭來實現訪問控制,所以它要求目標URL符合HTTP或HTTPS 協議,以便于XDR對象檢驗響應頭;
          2、只支持GET和POST請求;
          3、不能設置自定義請求頭信息,也不能訪問響應頭部信息;
          4、只支持text/plain作為請求頭Content-Type的取值:在XDR中,不管是GET還是POST請求,Content-Type被限制成“text/plain”,所以服務端不會把請求主體數據解析成鍵值對,即不能從參數中獲取到POST的數據,只能讀取流數據,需要其自行解析,如:

          var xdr = new XDomainRequest();
          xdr.open("POST", "xdrpost.php");
          xdr.onload = function(){
          console.log(xdr.responseText);
          };
          var param = "username=wangwei&age=18";
          xdr.send(param);

          xdrpost.php:

          <?php
          header("Access-Control-Allow-Origin: *");
          $content = file_get_contents("php://input");
          // echo $content; // username=王 唯&age=18
          $arr = explode("&", $content);
          foreach ($arr as $value) {
          $v = explode("=", $value);
          echo "key:$v[0], value:$v[1] \r\n";
          }

          5、身份驗證和cookie不能和請求一起發送,也不會隨響應返回;
          6、請求的URL必須和被請求的URL采用相同的協議:兩者的協議必須統一,要么是HTTP,要么是HTTPS;
          7、所有XDR請求都是異步執行的,不能用它來創建同步請求;

          Preflighted Requests:

          CORS通過一種叫做Prelighted Requests的透明服務器驗證機制支持開發人員使用自定義的頭部、GET或POST之外的請求方式,以及不同類型的主體內容;
          在使用下列高級選項來發送請求時,就會向服務器發送一個Preflight請求,這種請求使用OPTIONS方式,發送下列頭部:

          • Origin:與簡單的請求相同;
          • Access-Control-Request-Method:請求自身使用的方法;
          • Access-Control-Request-Headers:(可選)自定義的頭部信息,多個頭部以逗號分隔;

          以下是一個帶有自定義頭部customHeader,并使用POST方法發送的數據,如:

          • Origin: http://www.zeronetwork.cn
          • Access-Control-Request-Method: POST
          • Access-Control-Request-Headers: customHeader

          發送這個請求后,服務器端可以決定是否允許這種類型的請求,其通過在響應中發送如下頭部與瀏覽器進行溝通:

          • Access-Control-Allow-Origin:與簡單的請求相同;
          • Access-Control-Allow-Methods:允許的方法,多個方法以逗號分隔;
          • Access-Control-Allow-Headers:允許的頭部,多個頭部以逗號分隔;
          • Access-Control-Max-Age:應該將這個Preflight請求緩存多長時間(以秒表示)

          例如,允許任何源、POST請求方式、自定義頭customHeader以及請求的緩存時間:

          • Access-Control-Allow-Origin: https://www.zeronetwork.cn
          • Access-Control-Allow-Methods: POST
          • Access-Control-Allow-Headers: customHeader
          • Access-Control-Max-Age: 1728000

          如:

          var xhr = new XMLHttpRequest();
          xhr.onload = function(){
          console.log(xhr.responseText);
          }
          xhr.open("OPTIONS","https://www.b.com/flighted.php",true);
          xhr.setRequestHeader("customHeader", "customValue");
          xhr.send(null);

          后端flighted.php:

          <?php
          header("Access-Control-Allow-Origin: *");
          header("Access-Control-Allow-Headers: *");
          header("Access-Control-Allow-Methods: *");
          header("Access-Control-Max-Age: 1728000");
          echo "有Headers、Methods頭信息";

          Preflight請求結束后,結果將按照響應中指定的時間緩存起來;

          帶憑據的請求:

          默認情況下,跨域請求不提供憑據(cookie、HTTP認證及客戶端SSL證明等);
          通過將XHR對象的withCredentials屬性設置為true,可以指定某個跨域請求應該發送憑據(授權信息);如:

          xhr.withCredentials = true;
          xhr.send(null);

          當使用帶有憑據的請求時,不能把Access-Control-Allow-Origin設為*,并且Access-Control-Allow-Origin只能設置一個域,不能是多個,否則會拋出異常;

          后端c.com/credentials.php:

          header("Access-Control-Allow-Origin: http://www.a.com");
          echo "c.com/example.php,已經設置了ACAO";

          如果服務端接受帶憑據的請求,必須設置Access-Control-Allow-Credentials: true響應頭;

          如后端c.com/ credentials.php:

          header("Access-Control-Allow-Origin: http://www.a.com");
          header("Access-Control-Allow-Credentials: true");
          echo "設置了Origin,也設置了Credentials";
          echo json_encode($_COOKIE);

          如果在同源下配置withCredentials,無論配置true還是false,效果都會相同,且會一直提供憑據信息;另外,同時還可以發送自定義請求頭,如后端credentials.php:

          <?php
          header("Access-Control-Allow-Origin: http://www.a.com");
          header("Access-Control-Allow-Credentials: true");
          header("Access-Control-Allow-Headers: customHeader");
          echo "設置了Origin,也設置了Credentials";
          echo json_encode($_COOKIE);

          服務端還可以在Preflight響應中發送這個HTTP頭部,但不能把Access-Control-Allow-Headers設為*;

          跨瀏覽器的CORS:

          即使瀏覽器對CORS的支持程度并不一致,但所有瀏覽器都支持簡單的請求(非Preflight和不帶憑據的請求),因此有必要實現一個跨瀏覽器的方案:檢測XHR是否支持CORS的最簡單方式,就是檢查是否存在withCredentials屬性,再結合檢測XDomainRequest對象是否存在,就可以兼顧所有瀏覽器了,如:

          function createCORSRequest(method, url, withCredentials){
          var xhr = new XMLHttpRequest();
          if ("withCredentials" in xhr) {
          xhr.open(method, url);
          xhr.withCredentials = withCredentials;
          }else if(typeof XDomainRequest != "undefined"){
          xhr = new XDomainRequest();
          xhr.open(method, url);
          }else{
          xhr = null;
          }
          return xhr;
          }
          var request = createCORSRequest("GET", "https://www.b.com/credentials.php", true);
          if(request){
          request.onload = function(){
          console.log(request.responseText);
          };
          request.send(null);
          }

          示例:使用HEAD和CORS請求鏈接的詳細信息,如:

          var supportsCORS = (new XMLHttpRequest).withCredentials != undefined;
          var links = document.getElementsByTagName("a");
          for(var i=0; i<links.length; i++){
          var link = links[i];
          if(!link.href) continue;
          if(link.title) continue;
          if(link.host !== location.host || link.protocol != location.protocol){
          link.title = "站外鏈接";
          if(!supportsCORS) continue;
          }
          if(link.addEventListener)
          link.addEventListener("mouseover", mouseoverHandler, false);
          else
          link.attachEvent("onmouseover", mouseoverHandler);
          }
          function mouseoverHandler(e){
          var link = e.target || e.srcElement;
          var url = link.href;
          var xhr = new XMLHttpRequest();
          xhr.open("HEAD", url);
          xhr.onreadystatechange = function(){
          if(xhr.readyState !== 4) return;
          if(xhr.status == 200){
          var type = xhr.getResponseHeader("Content-Type");
          var size = xhr.getResponseHeader("Content-Length");
          var date = xhr.getResponseHeader("Last-Modified");
          link.title = "類型:" + type + "\n" +
          "大?。?#34; + size + "\n" +
          "時間:" + date;
          }else{
          if(!link.title)
          link.title = "獲取不到詳細信息:\n" +
          xhr.status + " " + xhr.statusText;
          }
          };
          xhr.send(null);
          if(link.removeEventListener)
          link.removeEventListener("mouseover", mouseoverHandler, false);
          else
          link.detachEvent("onmouseover", mouseoverHandler);
          }

          HTML:

          <a href="https://www.zeronetwork.cn/edu/">edu</a>
          <a href="https://www.zeronetwork.cn/study/">study</a>
          <a href="http://www.a.com/ demo.html">a.com</a>
          <a>no href</a>
          <a href="https://www.apple.com" title="baidu">apple.com</a>
          <a href="https://cn.bing.com">bing</a>

          其它跨域技術:

          雖然CORS技術已經無處不在,但在CORS出現之前,就已經存在一些跨域的技術了,雖然這些技術應用起來有些麻煩,但它們絕大部分不需要修改服務器端代碼,所以直到現在這些技術仍然被廣泛使用;

          后端代理方式:

          這種方式可以解決所有跨域問題,也就是將本域的后端程序作為代理,每次對其它域的請求都轉交給該代理程序,其通過模擬http請求去訪問其它域,再將返回的結果返回給前端,這樣做的好處是,無論訪問的是文檔、還是JS文件都可以實現跨域;
          例如,b.com/data.php響應JSON字符串:

          <?php
          $json_str = '{"name":"wagwei","sex":true,"age":18}';
          echo $json_str;

          a.com/getdata.php服務端獲取b.com/data.php響應:

          <?php
          // 創建cURL資源
          $ch = curl_init();
          // 設置URL和相應的選項
          curl_setopt($ch, CURLOPT_URL, "http://www.b.com/data.php");
          curl_setopt($ch, CURLOPT_HEADER, 0);
          // 抓取URL并把它傳遞給瀏覽器
          curl_exec($ch);
          // 關閉cURL資源,并釋放系統資源
          curl_close($ch);

          a.com/data.html使用Ajax請求同源的getdata.php:

          var xhr = new XMLHttpRequest();
          xhr.open("GET", "getdata.php");
          xhr.onreadystatechange = function(){
          if(xhr.readyState == 4 && xhr.status == 200){
          console.log(xhr.responseText);
          }
          };
          xhr.send(null);

          基于iframe實現跨域:

          基于iframe實現的跨域要求兩個域屬于同一個根域,如:www.a.com和b.a.com其使用同一協議(例如都是 http)和同一端口(例如都是80),此時在兩個頁面中同時設置document.domain為同一個主域,就實現了同域,從而可以實現通信;如b.a.com中的iframe.html:

          <h1>iframe</h1>
          <img src="images/hu.png" />
          <script>
          document.domain = "a.com";
          function show(msg){
          alert("收到的:" + msg);
          }
          if(parent.parentFun){
          parent.parentFun();
          }
          </script>

          www.a.com主頁面為:

          <script>
          function parentFun(){
          alert("parentFun");
          }
          </script>
          <iframe src="http://b.a.com/iframe.html" id="myframe"></iframe>
          <script>
          var myframe = document.getElementById("myframe");
          document.domain = "a.com";
          myframe.onload = function(){
          var win = myframe.contentWindow;
          console.log(win);
          win.show("零點程序員");
          var doc = myframe.contentDocument;
          console.log(doc);
          }
          </script>

          使用window.name和iframe進行跨域:

          window的name屬性返回的是該window的名稱,它的值有個特點:在不同的頁面(甚至不同域名)加載后依舊存在,并且可以支持非常長的name值(2MB),即在一個窗口(window)的生命周期內,窗口載入的所有的頁面都是共享一個window.name,每個頁面對window.name都有讀寫的權限;

          正因為window的name屬性的這個特征,所以可以使用window.name來進行跨域;例如a.html:

          <h1>a.html</h1>
          <script>
          window.name = "頁面a設置的name值";
          setTimeout(function(){
          window.location = "b.html";
          },3000); // 3秒后在當前window中載入新的頁面
          </script>

          b.html:

          <h1>b.html</h1>
          <script>
          alert(window.name); // 頁面a設置的name值
          </script>

          跨域:例如,有一個a.com/a.html頁面,需要通過js來獲取位于另一個不同域上的頁面,如:b.com/b.html里的數據:

          <script>
          window.name = "b.com/b.html中的數據";
          </script>

          如果b.html不跳轉,其他頁也可以獲取數據,可以采用iframe;
          如a.com/a.html:

          <h1>a.html</h1>
          <iframe id="iframe" src="http://www.b.com/b.html" style="display:none"></iframe>
          <script>
          var iframe = document.getElementById("iframe");
          // iframe在一開始載入b.com/b.html會執行此函數
          iframe.onload = function(){
          // 當iframe.src為b.html時觸發,此時iframe和當前頁面已經同源,可以訪問
          iframe.onload = function(){
          var data = iframe.contentWindow.name;
          alert(data);
          };
          // 這里的b.html為隨便一個頁面,只要與當前頁面同源就可以,
          // 目錄是讓iframe與當前頁面同源
          iframe.src = "b.html";
          }
          </script>

          使用location.hash+iframe跨域:

          假設a.com/a.html要向b.com/b.html傳遞信息;如a.com/a.html:

          <h1>a.html</h1>
          <script>
          function checkHash(){
          try{
          var data = location.hash ? location.hash.substring(1) : '';
          console.log("收到的數據是:" + data);
          }catch(e){}
          }
          setInterval(checkHash, 5000);
          window.onload = function(){
          var iframe = document.createElement("iframe");
          // iframe.style.display = "none";
          iframe.src = "http://www.b.com/b.html#param"; // 傳遞的location.hash
          document.body.appendChild(iframe);
          };
          </script>

          b.com/b.html:

          <h1>b.html</h1>
          <script>
          function checkHash(){
          var data = "";
          // 模擬一個簡單的參數處理操作
          switch(location.hash){
          case "#param":
          data = "somedata";
          break;
          case "#other":
          // ...
          break;
          default:
          break;
          }
          data && callBack("#" + data);
          }
          function callBack(hash){
          // ie、chrome的安全機制無法修改parent.location.hash
          //所以要利用一個中間的www.csdnblogs.com域下的代理iframe
          var proxy = document.createElement("iframe");
          proxy.style.display = "none";
          proxy.src = "http://www.a.com/c.html" + hash;
          // 注意該文件在a.com中
          document.body.appendChild(proxy);
          }
          window.onload = checkHash;
          </script>

          a.com/c.html:

          <script>
          //因為parent.parent和自身屬于同一個域,所以可以改變其location.hash的值
          parent.parent.location.hash = self.location.hash.substring(1);
          </script>

          圖像Ping:

          使用<img>標簽,也可以動態創建圖像,使用它們的onload和onerror事件處理程序來確定是否接收到了響應;例如:

          var img = new Image();
          img.onload = img.onerror = function(){
          console.log("Done");
          };
          img.src = "https://www.zeronetwork.cn/study/pingimg.php?name=wangwei";
          pingimg.php:
          if($_GET['name']){
          echo $_GET['name'];
          }

          圖像Ping有兩個主要的缺點,一是只能發送GET請求,二是無法訪問服務器的響應文本,因此,圖像Ping只能用于瀏覽器與服務器間的單向通信;提交的數據是通過查詢字符串形式發送的,但響應可以是任意內容,但通常是像素圖或204響應;
          通過圖像Ping,瀏覽器得不到任何具體的數據,但通過偵聽load和error事件,它能知道響應是什么時候接收到的,此時可以實現一些自身的邏輯;
          示例:圖像Ping最常用于跟蹤用戶點擊頁面或動態廣告曝光次數,如:

          <input type="button" id="btn" value="圖像Ping請求" />
          <div id="result"></div>
          <script>
          var increment = (function(){
          var counter = 0;
          return function(){
          return ++counter;
          };
          })();
          var btn = document.getElementById("btn");
          btn.addEventListener("click", function(event){
          var sum = increment();
          var result = document.getElementById("result");
          var img = result.getElementsByTagName("img")[0];
          if(!img)
          img = new Image();
          img.onload = img.onerror = function(){
          result.appendChild(img);
          var oSpan = document.getElementById("sum");
          if(!oSpan){
          oSpan = document.createElement("span");
          oSpan.id = "sum";
          }
          oSpan.innerHTML = "發送請求的次數:" + sum;
          result.appendChild(oSpan);
          };
          if(sum % 2)
          img.src = "https://www.zeronetwork.cn/study/images/ad1.jpg?sum=" + sum;
          else
          img.src = "https://www.zeronetwork.cn/study/images/ad2.jpg?sum="+sum;
          </script>

          基于<script>標簽實現跨域:

          在某些HTML元素中,可以通過它的src屬性跨域請求內容,例如img、iframe等,也就是沒有跨域的限制;同樣,script也可以,也就是利用script來執行跨域的javascript代碼,從而實現前端跨域請求數據的目的;例如:

          <script>
          var script = document.createElement('script');
          script.src = "http://www.b.com/scripts/demo.js";
          document.body.appendChild(script);
          script.onload = function(){
          show("從a.com傳過去的數據");
          }
          </script>

          b.com/scripts/demo.js:

          function show(msg){
          alert("收到的數據:" + msg);
          }
          alert("www.b.com/script/demo.js");

          JSONP:
          JSONP是JSON with padding(填充式JSON或參數式JSON)的簡寫,是應用JSON的新方法,其利用<script>標簽沒有跨域限制的特點,可以得到從其他源動態產生的JSON數據,但JSONP請求一定需要對方服務器的支持才可以;
          JSONP看起來與JSON差不多,是被包含在函數調用中的JSON,形如:callback({“name”: “wangwei”});
          JSONP由兩部分組成:回調函數callback和json數據;回調函數是當響應到來時應該在頁面中調用的函數,其名字一般是在請求中指定的,需要在本地實現;而數據就是傳入回調函數中的JSON數據;整個JSONP就是一個標準的JavaScript語句;
          JSONP后端服務:被請求服務端程序必須提供JSONP的服務,一般情況下,其會返回標準的JSONP;
          例如,被請求服務端b.com/jsonptest.php返回JSONP的簡單形式:

          <?php
          echo 'alert({"name":"王唯","age":18})';

          在請求端中使用<script>引入該文件,如:

          <script src="http://www.b.com/jsonptest.php"></script>

          請求端和服務端共同約定使用自定義函數,而不是內置函數;例如b.com/jsonptest.php:

          echo 'handlerJSONP({"name":"王唯","age":18})';

          請求端實現handlerJSONP()函數,并使用<script>引入b.com/jsonptest.php文件,如:

          <script>
          function handlerJSONP(response){
          alert("姓名:" + response.name + "\n" + "年齡:" + response.age);
          }
          </script>
          <script src="http://www.b.com/jsonptest.php"></script>

          一般來說,后端程序通過查詢字符串允許客戶端指定一個函數名,然后用這個函數名去填充響應,例如:

          <script>
          function handlerJSONP(response){
          alert(response);
          }
          </script>
          <script src="http://www.b.com/jsonptest.php?callback=handlerJSONP"></script>

          b.com/jsonptest.php:

          header('Content-type: application/json');
          //獲取回調函數名
          $callback = htmlspecialchars($_REQUEST['callback']);
          //json數據
          $json_data = '["王唯","靜靜","娟子","大國"]';
          //輸出jsonp格式的數據
          echo $callback . "(" . $json_data . ")";

          JSONP是通過動態<script>元素來使用的,例如:

          // ...
          var script = document.createElement("script");
          script.src = "http://www.b.com/jsonptest.php?callback=handlerJSONP";
          document.body.insertBefore(script, document.body.firstChild);

          請求端與后端就該函數名有個約定,使用隨機名,如:hander1234();而查詢字符串的名字一般約定使用”json”或”callback”,當然也可以是其他任意的名稱;

          JSONP之所以在開發人員中極為流行,主要原因是它非常簡單易用;與圖像Ping相比,它的優點在于能夠直接獲取響應文本,支持在瀏覽器與服務器之間雙向通信;

          在某些時候,處理完JSONP數據后,動態創建的<script>就可以刪除了;

          function clickHandler(e){
          var script = document.createElement("script");
          script.id = "jsonp_script";
          script.src = "http://www.a.com/jsonp.php?callback=handlerResponse";
          document.body.insertBefore(script, document.body.firstChild);
          }
          function handlerResponse(response){
          console.log(response);
          var script = document.getElementById("jsonp_script");
          script.parentNode.removeChild(script);
          }
          var btn = document.getElementById("btn");
          btn.addEventListener("click", clickHandler);

          定義將被腳本執行的回調函數

          getJSONP[cbnum] = function(response){
          try{
          callback(response);
          }finally{
          delete getJSONP[cbnum];
          script.parentNode.removeChild(script);
          }
          };
          script.src = url;
          document.body.appendChild(script);
          }
          getJSONP.counter = 0; // 用于創建唯一回調函數名稱的計數器
          function handlerResponse(response){
          console.log(response);
          }
          getJSONP("http://www.a.com/jsonp.php", handlerResponse);

          示例:某接口的應用:

          <input type="button" id="btn" value="jsonp請求">
          <script>
          var btn = document.getElementById("btn");
          btn.addEventListener("click", function(){
          var script = document.createElement("script");
          script.id = "jsonscript";
          script.src = "https://suggest.taobao.com/sug?code=utf-8&q="+ encodeURIComponent("衣服") +"&callback=jsonpCallback";
          document.body.appendChild(script);
          });
          function jsonpCallback(response){
          console.log(response);
          var script = document.getElementById("jsonscript");
          script.parentNode.removeChild(script);
          }
          </script>

          JSONP的不足:

          • 首先,JSONP是從其他域中加載代碼執行,如果其他域不安全,很可能會在響應中夾帶一些惡意代碼;因此在使用不是你自己運維的Web服務時,一定得保證它安全可靠;
          • 其次,只能進行GET請求;
          • 最后,要確定JSONP請求是否失敗比較麻煩,雖然HTML5給<script>元素新增了一個onerror事件處理程序,但目前還沒有得到任何瀏覽器支持;

          JSONP和AJAX對比:
          JSONP和AJAX相同,都是客戶端向服務器端發送請求,從服務器端獲取數據的方式;但AJAX屬于同源策略,采用CORS方案跨域,而JSONP屬于非同源策略進行跨域請求;

          window.postMessage()方法:

          window.postMessage()方法可以安全地實現跨域通信;其是HTML5規范提供的一種受控機制來規避跨域安全限制的方法,采用異步的方式進行有限的通信,既可以用于同域傳遞消息,也可以用于跨域傳遞消息;

          其應用的場景是:

          • 頁面和其打開的新窗口的數據傳遞;
          • 多窗口之間消息傳遞;
          • 頁面與嵌套的iframe消息傳遞;

          一個窗口可以獲得對另一個窗口的引用,比如iframe的contentWindow屬性、執行window.open返回的窗口對象、或者是命名過或數值索引的window.frames,然后在窗口上調用targetWindow.postMessage()方法分發一個MessageEvent消息;接收消息的窗口觸發onmessage事件,并接收分發過來的消息;

          例如a.com/post.html,獲取iframe:

          <h1>a.com/post.html</h1>
          <iframe id="iframe" src="http://www.b.com/message.html"></iframe>
          <script>
          var iframe = document.getElementById("iframe");
          var win = iframe.contentWindow;
          console.log(win);
          </script>

          b.com/ message.html:

          <h1>b.com/message.html</h1>

          語法:otherWindow.postMessage(message, targetOrigin, [transfer]);

          • otherWindow為其他窗口的一個引用;
          • message參數:將要發送到其他window的數據;它將會被結構化克隆算法序列化,即可以不受限制的將數據對象安全的傳送給目標窗口而無需自己序列化;
          • targetOrigin:通過窗口的origin屬性來指定哪些窗口能接收到消息事件,其值可以是字符串"*"(表示無限制)或者一個URI;
          • transfer參數:可選,是一串和message同時傳遞的Transferable對象,這些對象的所有權將被轉移給消息的接收方,而發送一方將不再保有所有權;

          例如:a.com/post.html:

          // ...
          win.postMessage("來自a.com/post.html的消息","*");
          b.com/message.html:
          window.addEventListener("message", function(event){
          console.log(event); // MessageEvent
          });

          需要延遲執行postMessage()方法,延遲的方式有多種,使用setTimeout()、iframe的onload事件、發送者的onload事件或者使用按鈕的click事件處理程序,如a.com/post.html:

          setTimeout(function(){
          win.postMessage("來自a.com/post.html的消息","*");
          },500);
          // 或:
          var iframe = document.getElementsByTagName("iframe")[0];
          iframe.onload = function() {
          var iframe = document.getElementById("iframe");
          var win = iframe.contentWindow;
          win.postMessage("來自a.com/post.html的消息","*");
          }
          // 或:
          var btnSend = document.getElementById("btnSend");
          btnSend.addEventListener("click", function(){
          var iframe = document.getElementsByTagName("iframe")[0];
          var win = iframe.contentWindow;
          win.postMessage("來自a.com/post.html的消息","*");
          });

          使用postMessage()將數據發送到其他窗口時,最好指定明確的targetOrigin,而不是*,原因是惡意網站可以在用戶不知情的情況下更改窗口的位置,甚至可以攔截所發送的數據;

          win.postMessage("來自a.com/post.html的消息","http://www.b.com"); // 或
          win.postMessage("來自a.com/post.html的消息","http://www.b.com/"); // 或
          win.postMessage("來自a.com/post.html的消息","http://www.b.com/error.html");

          在發送消息的時候,如果目標窗口的協議、主機地址或端口這三者的任意一項不匹配targetOrigin的值,那么消息就不會被發送;只有三者完全匹配,消息才會被發送;這個機制用來控制消息可以發送到哪些窗口。

          MessageEvent接口:
          代表一段被目標對象接收的消息;
          屬性:

          • data屬性:其保存著由發送者發送的字符串數據;
          • lastEventId屬性:表示事件的唯一ID;
          • origin屬性:返回一個表示消息發送者來源;
          • ports屬性:MessagePort對象數組,表示消息正通過特定通道(數據通道)發送的相關端口;
          • source屬性:是一個MessageEventSource對象,代表消息發送者;
          window.addEventListener("message", function(event){
          console.log(event);
          console.log(event.data); // 來自a.com/post.html的消息
          console.log(event.lastEventId); // 空
          console.log(event.origin); // http://www.a.com
          console.log(event.ports); // []
          console.log(event.source); // window http://www.a.com/post.html
          });

          如果不希望接收message,就不要實現message事件;如果希望從其他網站接收message,最好使用origin或source屬性驗證消息發送者的身份;如:

          window.addEventListener("message", function(event){
          if(event.origin != "http://www.a.com")
          return;
          console.log(event.data);
          });

          雖然postMessage()是單向的通信,但可以使用source屬性在具有不同origin的兩個窗口之間建立雙向通信;
          例如a.com/message.html:

          <iframe id="iframe" src="http://www.b.com/message.html"></iframe>
          <input type="button" id="btn" value="send" />
          <script>
          var btn = document.getElementById("btn");
          btn.addEventListener("click", function(){
          var iframe = document.getElementById("iframe");
          var win = iframe.contentWindow;
          win.postMessage("來自a.com/post.html的消息","http://www.b.com/");
          });
          window.addEventListener("message", function(event){
          console.log("a.com收到:" + event.data);
          });
          </script>

          b.com/message.html:

          window.addEventListener("message", function(event){
          if(event.origin != "http://www.a.com")
          return;
          console.log("b.com收到:" + event.data);
          event.source.postMessage("b.com/message回發的消息", event.origin);
          });

          與使用open()方法打開的窗口通信:
          在向使用open()方法打開的窗口發送消息時,需要延遲執行,如:

          btnOpen.addEventListener("click", function(){
          var win = window.open("http://www.b.com/message.html","_blank","width:600px,height:400px");
          setTimeout(function(){
          win.postMessage("從a.com中打開","http://www.b.com");
          },1000);
          });
          window.addEventListener("message", function(event){
          console.log("a.com收到:" + event.data);
          });

          或者反向發送消息,例如a.com/post.html:

          var btnOpen = document.getElementById("btnOpen");
          btnOpen.addEventListener("click", function(){
          var win = window.open("http://www.b.com/message.html","_blank","width:600px,height:400px");
          });
          window.addEventListener("message", function(event){
          console.log("a.com收到:" + event.data);
          });

          b.com/message.html:

          <script>
          window.onload = function(){
          var targetWindow = window.opener;
          console.log(targetWindow);
          targetWindow.postMessage("message.html is ready","http://www.a.com");
          }
          window.addEventListener("message", function(event){
          console.log("b.com收到:");
          console.log(event.data);
          event.source.postMessage("b.com/message.html回發的消息", event.origin);
          });
          </script>

          postMessage()還可以發送結構化數據,該數據將會自動被(結構化克隆算法)序列化,如:

          var btn = document.getElementById("btn");
          btn.addEventListener("click", function(){
          var iframe = document.getElementById("iframe");
          var win = iframe.contentWindow;
          var person = {
          name:"wanwei",
          sex: true,
          age: 18,
          friends: ["jingjing","daguo"],
          // smoking: function(){console.log(this.name)}, // 異常 could not be cloned.
          other1: undefined,
          other2: null
          }
          win.postMessage(person,"http://www.b.com/");

          b.com/message.html:

          window.addEventListener("message", function(event){
          console.log("b.com收到:");
          console.log(event.data);
          });

          因為是跨域,所以即使取得外源窗口的window對象,也無法操作對方的DOM,但是通過接收到的消息,自行構建DOM;

          例如a.com/post.html:

          var btn = document.getElementById("btn");
          btn.addEventListener("click", function(){
          var iframe = document.getElementById("iframe");
          var win = iframe.contentWindow;
          var person = {
          name:"wanwei",
          sex: true,
          age: 18,
          friends: ["jingjing","daguo"],
          }
          win.postMessage(person,"http://www.b.com/");
          });
          window.addEventListener("message", function(event){
          if(event.origin == "http://www.b.com"){
          if(event.data.state){
          document.getElementById("btn").setAttribute("disabled",true);
          // ...
          }
          }
          });
          b.com/message.html:
          window.addEventListener("message", function(event){
          if(event.origin == "http://www.a.com"){
          var person = event.data;
          var h1 = document.createElement("h1");
          h1.innerText = person.name + "信息";
          document.body.appendChild(h1);
          var p = document.createElement("p");
          p.innerHTML = "性別:" + (person.sex ? "男" : "女");
          p.innerHTML += "<br/>年齡:" + person.age;
          p.innerHTML += "<br/>朋友:" + person.friends.join(",");
          document.body.appendChild(p);
          event.source.postMessage({state:1}, event.origin);
          }
          });

          示例,后臺管理的應用,main.html:

          <style>
          *{margin:0; padding:0;}
          ul,li{list-style: none;}
          .top{width:100%; height:100px; background-color:yellowgreen;}
          .left{width:20%; height: 100%; float: left; background-color:yellow;}
          .left ul li{padding:10px;}
          .left ul li a{color:#000; text-decoration: none;}
          iframe{width:80%; float: right; border:none;}
          .bg{
          position: fixed; left:0; top: 0; display: none;
          width:100%; height:100%; background-color: rgba(0, 0, 0, .5);
          }
          .showBg{display: block !important}
          .confirm{
          position:fixed; width:400px; height:200px; z-index: 2;
          left: 50%; top: 50%; transform: translate(-50%, -50%);
          padding: 20px; text-align: center; background-color: #FFF;
          }
          </style>
          <div class="top"></div>
          <div class="left">
          <ul>
          <li><a href="console.html" target="iframe">控制臺</a></li>
          <li><a href="content.html" onclick="sendMessage()" target="iframe">系統設置</a></li>
          </ul>
          </div>
          <iframe name="iframe" src="console.html"></iframe>
          <div class="bg">
          <div class="confirm">
          <p>是否確認保存?</p>
          <p><input type="button" id="btnCancel" value="取消" />
          <input type="button" id="btnSave" value="保存" /></p>
          </div>
          </div>
          <script>
          var iframe = null;
          window.onload = function(){
          var leftDiv = document.getElementsByClassName("left")[0];
          iframe = document.getElementsByTagName("iframe")[0];
          iframe.style.height = leftDiv.style.height = (document.documentElement.scrollHeight - 100) + "px";
          }
          function sendMessage(){
          iframe.onload = function(){
          iframe.contentWindow.postMessage({
          method: 'dataId',
          data: {dataId: 1}
          }, "*");
          }
          }
          window.addEventListener("message", function(event){
          if(event.data.method == "showBg"){
          document.getElementsByClassName("bg")[0].classList.add("showBg");
          }
          });
          var btnCancel = document.getElementById("btnCancel");
          btnCancel.addEventListener("click", function(){
          if(iframe){
          iframe.contentWindow.postMessage({
          method: "cancel"
          }, "*");
          document.getElementsByClassName("bg")[0].classList.remove("showBg");
          }
          });
          var btnSave = document.getElementById("btnSave");
          btnSave.addEventListener("click", function(){
          if(iframe){
          iframe.contentWindow.postMessage({
          method: "save"
          }, "*");
          document.getElementsByClassName("bg")[0].classList.remove("showBg");
          }
          });
          </script>

          console.html:

          <h1>控制臺</h1>

          content.html:

          <style>
          .container{width:60%; margin: 0 auto;}
          </style>
          <div class="container">
          <p>設置1:<input type="text" /></p>
          <p>設置2:<input type="text" /></p>
          <p>設置3:<input type="text" /></p>
          <p>設置4:<input type="text" /></p>
          <p><input type="button" id="btnConfirm" value="保存" /></p>
          </div>
          <script>
          window.addEventListener("message", function(event){
          if(event.data.method == "dataId"){
          console.log(event.data.data.dataId);
          }else if(event.data.method == "cancel"){
          console.log("取消操作");
          }else if(event.data.method == "save"){
          console.log("保存成功");
          }
          });
          var btnConfirm = document.getElementById("btnConfirm");
          btnConfirm.addEventListener("click", function(){
          window.parent.postMessage({
          method: "showBg"
          }, "*");
          });
          </script>

          示例,修改信息:

          content.html:
          <div class="container">
          <h1>用戶信息</h1>
          <p>ID:001</p>
          <p>姓名:<span id="usernameSpan">王唯</span>
          <input type="button" id="editInfo" value="修改個人信息" /> </p>
          <p>單位:<span id="jobSpan">零點網絡</span>
          <input type="button" id="editJob" value="修改工作信息" /></p>
          <p>地址:<input type="text" id="address" value="北京市東城區" /></p>
          <p>電話:<input type="text" id="tel" value="13888888888" /></p>
          <p><input type="button" id="btnConfirm" value="保存" /></p>
          </div>
          <script>
          window.addEventListener("message", function(event){
          if(event.data.method == "dataId"){
          console.log(event.data.data.dataId);
          }else if(event.data.method == "cancel"){
          console.log("取消操作");
          }else if(event.data.method == "save"){
          document.getElementById("usernameSpan").innerText = event.data.data.username;
          console.log("保存成功");
          }
          });
          var btnConfirm = document.getElementById("btnConfirm");
          btnConfirm.addEventListener("click", function(){
          window.parent.postMessage({
          method: "showBg"
          }, "*");
          });
          var editInfo = document.getElementById("editInfo");
          editInfo.addEventListener("click", function(){
          var win = window.open("editInfo.html", "_blank","width:200px,height:500px");
          setTimeout(function(){
          win.postMessage({
          method: "info",
          dataId: 2
          });
          },500);
          });
          </script>

          editInfo.html:

          <form>
          <input type="hidden" id="ID" name="ID" />
          <p>姓名:<input type="text" id="username" name="username" /></p>
          <p>性別:<input type="radio" id="male" name="sex" value="1" />男
          <input type="radio" id="female" name="sex" value="0" />女</p>
          <p>年齡:<input type="text" id="age" name="age" /></p>
          <p><input type="button" id="btnCancel" value="取消" />
          <input type="button" id="btnSave" value="保存" /></p>
          </form>
          <script>
          window.addEventListener("message", function(event){
          if(event.data.method == "info"){
          // Ajax請求,從數據庫中取出這條記錄
          var id = event.data.dataId;
          var xhr = new XMLHttpRequest();
          xhr.open("GET", "getInfo.php?action=showInfo&ID=" + id);
          xhr.onreadystatechange = function(){
          if(xhr.readyState == 4 && xhr.status == 200){
          // console.log(xhr.response);
          var person = xhr.response;
          document.forms[0].elements['ID'].value = person.ID;
          document.forms[0].elements['username'].value = person.username;
          document.forms[0].elements['sex'].value = person.sex;
          document.forms[0].elements['age'].value = person.age;
          xhr = null;
          }
          };
          xhr.responseType = "json";
          xhr.send(null);
          }
          });
          var btnCancel = document.getElementById("btnCancel");
          btnCancel.addEventListener("click", function(){
          window.opener.postMessage({method: "cancel"}, "*");
          window.close();
          });
          var btnSave = document.getElementById("btnSave");
          btnSave.addEventListener("click", function(){
          // 保存到數據庫
          var data = new FormData(document.forms[0]);
          data.append("action", "update");
          var xhr = new XMLHttpRequest();
          xhr.open("POST", "getInfo.php");
          xhr.onreadystatechange = function(){
          if(xhr.readyState == 4 && xhr.status == 200){
          var data = JSON.parse(xhr.responseText);
          if(data.status){
          var person = {
          ID: document.forms[0].elements['ID'].value,
          username: document.forms[0].elements['username'].value,
          sex: document.forms[0].elements['sex'].value,
          age: document.forms[0].elements['age'].value
          };
          window.opener.postMessage({
          method: "save",
          data: person
          });
          }
          xhr = null;
          }
          };
          xhr.send(data);
          window.close();
          })
          </script>

          editInfo.php:

          <?php
          require_once "conn.php";
          if(isset($_REQUEST['action'])){
          if($_REQUEST['action'] == 'showInfo'){
          $ID = intval($_GET['ID']);
          $sql = "select ID,username, sex, age from users where ID=$ID";
          $result = $conn->query($sql);
          $row = mysqli_fetch_array($result);
          echo json_encode($row);
          }elseif($_REQUEST['action'] == 'update'){
          $ID = intval($_POST['ID']);
          $username = $_POST['username'];
          $sex = $_POST['sex'];
          $age = $_POST['age'];
          $sql = "update users set username='$username', sex='$sex', age='$age' where ID=$ID";
          $result = $conn->query($sql);
          if($result){
          echo '{"status": 1}';
          }else{
          echo '{"status": 0}';
          }
          }

          超鏈接打開的窗口也可使用postMessage()方法進行通信,但需要設置a標簽的target屬性為自定義值,如:

          <p><a href="http://www.b.com/content.html" target="mywin">零點程序員</a></p>
          b.com/content.html:
          console.log(window.opener); // Window or global
          console.log(window.name); // "mywin"

          發送消息,b.com/content.html:

          if(window.opener){
          console.log(window.opener);
          window.opener.postMessage("我已經打開了","*");
          }

          主頁面:

          /一、html

          //a、HTML語法規范

          //a.1基本語法概述

          1、HTML標簽是由尖括號包圍的關鍵字,例如,

          2、HTML標簽通常是成對出現的,例如和,我們成為雙標簽,標簽對中的第一個標簽是開始標簽,第二個標簽是結束標簽。

          3、有些特殊的標簽是單個標簽(極少情況),例如

          ,我們稱為單標簽

          //a.2標簽關系

          雙標簽關系可以分為兩類:包含關系和并列關系。

          //b、HTML基本結構標簽

          //b.1第一個HTML網頁

          每個網頁都會有一個基本的結構標簽(也稱為骨架標簽),頁面內容也是在這些基本標簽上寫的。

          HTML頁面也稱為HTML文檔

          標簽名

          定義

          說明

          <html></html>

          HTML標簽

          頁面中最大的標簽,我們稱為根標簽

          <head></head>

          文檔的頭部

          注意在head標簽中我們必須要設置的標簽是title

          <title></title>

          文檔的標題

          讓頁面擁有一個屬于自己的網頁標題

          <body></body>

          文檔的主體

          元素包含文檔的所有內容,頁面內容,基本都是放到body里面的

          必須是.html或.htm,瀏覽器的作用是讀取HTML文檔,并以網頁的形式顯示出它們。

          此時,用瀏覽器打開這個網頁,我們就可以預覽我們寫的第一個HTML文件了。

          //c、開發工具vscode

          1、<!DOCTYPE>標簽
          文檔類型聲明,作用就是告訴瀏覽器使用哪種HTML版本來顯示網頁

          <!DOCTYPE html>這句代碼的意思是:當前頁面采用的是HTML5來顯示頁面。
          

          2、lang語言

          用來定義當前文檔顯示的語言:

          a、en定義語言為英語

          b、zh-CN定義語言為中文

          簡單來說定義為en就是英文網頁,定義為zh-CN就是中文網頁

          其實對于文檔顯示來說,定義成en的文檔也可以顯示中文,定義zh-CN的文檔也可以顯示英文

          這個屬性對于瀏覽器和搜索引擎(百度、谷歌等)還是有作用的

          3、charset字符集

          字符集是多個字符的集合,以便計算機能夠識別和存儲各種文字

          在標簽內,通過標簽的charset屬性來規定HTML文檔應該使用哪種字符編碼。

          charset常用的值:GB2312、BIG5、GBK和UTF-8,其中UTF-8也稱為萬國碼,基本包含了全世界所有國家需要用到的字符。

          注意:上面語法是必須寫的代碼,否則可能引起亂碼的情況,一般情況下,統一使用"UTF-8"編碼,盡量統一寫成標準的"UTF-8",不要寫成"utf-8"或"UTF8"。

          //d、HTML常用標簽

          //d.1標簽語義

          學習標簽是有技巧的,重點是記住每個標簽的語義,簡單理解就是指標簽的含義,即這個標簽是用來干嘛的

          根據標簽的語義,在合適的地方給一個最為合理的標簽,可以讓頁面結構更清晰。

          //d.2標題標簽

          -

          (重要)

          為了使網頁更具有語義,我們經常會在頁面中用到標題標簽,HTML提供了6個等級的網頁標題集

          -

          特點:

          1、加了標題的文字會變得更加粗,字號也會依次變大。

          2、一個標題獨占一行。

          //d.3段落和換行標簽(重要)

          在網頁中,要把文字有條理地顯示出來,就需要將這些文字分段顯示,在HTML標簽中,

          標簽用于定義段落,它可以將整個網頁分為若干段落。

          <p>我是一個段落標簽</p>標簽語義:可以把HTML文檔分割為若干段落。
          

          特點:
          1、文本在一個段落中會根據瀏覽器窗口的大小自動換行。
          2、段落和段落之間保有一個較大的空隙。

          在HTML中,一個段落中的文字從左到右依次排列,直到瀏覽器的右端,然后自動換行,如果希望某段文本強制換行顯示,就需要使用換行標簽

          <br />
          

          單詞break的縮寫,意為打斷,換行。

          特點:

          1、單標簽

          2、

          標簽只是簡單地開始新的一行,跟段落不一樣,段落之間會插入一些垂直的間距。

          //d.4文本格式標簽

          在網頁中,有時需要為文字設置粗體,斜體或下劃線等效果,這時就需要用到HTML中的文本格式標簽,使文字以特殊的方式顯示

          標簽語義:突出重要性,比普通文字更重要。

          語義

          標簽

          說明

          加粗

          <strong></strong>或者<b></b>

          更推薦使用<strong></strong>標簽加粗,語義更強烈

          傾斜

          <em><em> 或者<i><i>

          更加推薦使用<em><em>標簽,語義更加強烈

          刪除線

          <del><del>或者<s><s>

          更加推薦使用<del><del>標簽,語義更加強烈

          下劃線

          <ins><ins>或者<u><u>

          更加推薦<ins><ins>標簽,語義更加強烈

          //d.5<div>和<span>標簽
          <div>和<span>是沒有語義的,它們就是一個盒子,用來裝內容。

          <div>這是頭部</div>
          <span>今日價格</span>

          div是division的縮寫,表示分割,分區,span意為跨度,跨距。

          特點:

          1、<div>標簽用來布局,但是現在一行只能放一個<div>,大盒子。

          2、<span>標簽用來布局,一行上可以有多個<span>,小盒子

          //d.6圖像標簽和路徑(重點)

          1、圖像標簽

          在HTML標簽中,<img>標簽用于定義HTML頁面中的圖像。

          <img src="圖像url"/>

          單詞image的縮寫,意為圖像

          src是<img>標簽的必須屬性,它用于指定圖像文件的路徑和文件名。

          所謂屬性:簡單理解就是屬于這個圖像標簽的特性。

          圖像標簽的其他屬性:

          屬性

          屬性值

          說明

          src

          圖片路徑

          必須屬性

          alt

          文本

          替換文本,圖像不能顯示的文字

          title

          文本

          提示文本,鼠標放到圖像上,顯示文字

          width

          像素

          設置圖像的寬度

          height

          像素

          設置圖像的高度

          border

          像素

          設置圖像的邊框粗細

          、HTML簡介

          1.HTML是什么?

          HTML:htper text markup language超文本標記(標簽)語言

          由各種標簽組成,用來制作網頁,告訴瀏覽器如何顯示頁面

          2.作用

          • 制作網頁,控制網頁和內容的顯示
          • 插入圖片、音樂、視頻、動畫等多媒體
          • 通過鏈接來檢索信息
          • 使用表單獲取用戶的信息,實現交互

          3.版本

          w3c:world wide web consortium萬維網聯盟,制定web技術相關標準和規范的組織,HTML技術hi由w3c制定的標準

          兩個版本:HTML4.0.1、HTML5.0-----通常H5

          官網:http://www.W3shcool.com.cn

          4.擴展名

          HTML文檔是以.html或.htm結尾

          二、HTML文檔結構

          1.基本結構

          1.1簡介

          • HTML標簽是由尖括號括起來的關鍵詞,如,通常是成對出現的,如<html></html>
          • <html>為根標簽,包含: <head>頭部和<body>主體部分
          • 頭部提供關于網頁的相關信息,如標題、文檔類型、字符編碼、關鍵字等摘要信息
          • 主體部分提供網頁的顯示內容,真正顯示在頁面中的內容
          • 合理地進行縮進
          • 標簽名不區分大小寫,但是一般要用小寫

          1.2.開發工具

          記事本notepad、sublime、Notepad++、Dreamweaver、VScode、Webstorm等

          使用步驟:

          1. 新建文件(cltr+N),然后保存(ctrl+s),指定擴展名為.html
          2. 編寫HTML代碼
          3. 在瀏覽器中打開文件

          使用技巧:

          • 先保存再寫代碼,否則代碼無顏色提示
          • 創建一個文件夾,用于保存所有的網頁內容,將文件夾拖拽到sublime中,便于管理
          • 顯示/隱藏側邊欄方式1:查看–>側邊欄–>顯示/隱藏側邊欄方式2:ctrl+K緊接著按B
          • 顯示多欄方式1:查看–>布局–>列數:2列方式2:Alt+shift+2

          1.3瀏覽器

          常見的瀏覽器:IE瀏覽器微軟、chrome谷歌瀏覽器、fifirefox火狐、safari蘋果

          瀏覽器的作用是讀取html文件,并以網頁的形式來顯示

          瀏覽器不會直接顯示html標簽,而是使用標簽來解釋網頁的內容

          2.標簽

          2.1標簽的組成

          一個完整的html標簽的組成:

          <標簽名 屬性名="屬性值">內容</標簽名>

          <!DOCTYPE html>
          <html lang="en">
          	<head>
          		<meta charset="UTF-8">
          		<title>標簽</title>
          	</head>
          	<body  bgcolor="red" text="blue">
          		html從入門到精通!
              </body>
          </html>
          12345678910

          屬性值要用雙撇號括起來,一般用雙引號

          2.2標簽的分類

          根據標簽是否關閉,分為,關閉型和非關閉型

          • 關閉型:有結束標簽,即標簽成對出現
          <html></html>
          <head></head>
          <title></title>

          非關閉型:沒有結束標簽

          <meta>
          <br>
          <h1>....<h6>

          根據標簽是否獨占一行,分為塊級標簽和行級標簽

          塊級標簽:顯示為塊狀,獨占一行

          <h1>大家好</h1>
          <hr>

          行級標簽:在行內顯示,可與其他內容在同一行顯示

          <span></span>

          2.3注釋

          注釋在瀏覽器中不會顯示,是用來標注解釋html語句,但通過查看源代碼的方式可以看到

          語法:

          <--注釋內容-->

          2.4實體字符

          也稱為特殊字符,用于顯示一些特殊符號,如<>&空格等

          語法:

          <&實體字符的名稱>

          2.5文檔類型

          在html文檔的第一行,使用<!DOCTYPE html>

          聲明HTML文檔的類型用來告訴瀏覽器頁面的文檔嘞型,用來制定html版本的規范

          目前基本上最常用的html5

          <!DOCTYPE html>
          <html lang="en">
          <head>
          	<meta charset="UTF-8">
          	<title>Document</title>
          </head>
          <body>
          	
          </body>
          </html>
          12345678910

          三\常用標簽

          3.1基本標簽

          • 塊級標簽\親啊后有明顯的間隔 |
            | h1…h5 | 標題標簽 | 按照h1到h6逐漸變小.塊級標簽 |
            | | | |
            | | | |


          • 三、常用標簽

          1.基本標簽

          1.1 有序列表
          ol:ordered listli:list item默認使用阿拉伯數字、從1開始標記,可以通過屬性進行修改
          · type屬性:設置列表的符號標記、取值;數字1(默認)、字母(a或A)、羅馬數字(i或I) · start屬性:設置起始值,值必須是數字
          1.2 無序列表
          ul:unodered list
          li:list item
          默認情況下使用實心圓表作為符號標記,可以通過屬性進行修改
          · type屬性:設置列表的符號標記、取值:disc實心圓(默認)、circle空心圓、square正方形、none不 顯示項目符號
          1.3 定義列表
          dl:definition list
          dt:definition title
          dd:definition description
          1.4 水平線標簽
          hr:horizontal
          常用屬性:
          · color:顏色

          兩種方式:

          顏色名稱:如red、green、blue、white、black、pink、orange等

          16進制的RGB表示法:Red、Green、Blue用法:#RRGGBB 每種顏色的取值范值0-255,轉換為16 進制00-FF

          如: #FF0000 紅色 #00FF00綠色 #0000FF藍色 #FFFFFF白色、#CCCCCC #FF7300桔色

          · size:粗細,數值

          · width寬度

          兩種寫法:

          ? 像素:絕對值(固定值)

          ? 百分比:相對值,相對于水平線標簽所在父容器寬度的百分比

          · align對齊

          ? 取值:center居中 left right
          1.5圖像標簽
          img:image
          常見的圖片格式:.jpg .png .gif .bmp
          常見的屬性:
          · src:source指定圖片的路徑(來源),必選叁數

          如果圖片與html源代碼在同一個文件夾中,可以直接在src中寫圖片名稱即可

          習慣上,我們會將多個圖片與html代碼文檔分別放在同一個文件夾project中的不同目錄下,此時需要 在src中指定圖片的路徑為相對路徑

          路徑的分類:

          ? · 相對路徑

          ? 表示: ./當前路徑
          …/當前位置的上一級文件夾

          ? 提示:…/image

          ? · alt:當圖片無法顯示時顯示的提示信息

          ? · title:當鼠標放到圖片上時顯示的提示信息

          ? · width和 height:設置圖片的寬度和高度
          默認圖片以原始尺寸顯示

          ? 如果只設置其中一個,則另一個會按比例縮放

          ? 如果同時設置寬和高,可能導致圖片變形

          ? 兩種寫法:

          ? 像素:絕對值(固定值)

          ? 百分比:相對值,相對于父容器的尺寸的百分比
          2.其他標簽

          為了更好語義化
          3.頭部標簽
          · meta定義網頁的摘要信息,如字符編碼,關鍵詞,描述,作者等
          · title定義網頁的標題
          · style定義內容css樣式
          · link引用外部css樣式
          · script定義或引用腳本
          · base定義基礎路徑
          默認以當前頁面文件所在的位置為相對路徑參照
          4.標簽嵌套
          一個標簽中嵌套另外一個標簽
          標簽不能亂嵌套
          瀏覽器渲染后顯示的頁面代碼與編碼時有所不同
          chrome瀏覽器提供的開發工具:幫助開發人員查看和調試頁面的
          如何打開:
          · Elements:從瀏覽器的角度來看頁面,瀏覽器渲染頁面時內部的結構
          · console:控制臺,顯示各種警告和錯誤信息
          · network:查看網絡請求信息,瀏覽器向服務器請求了哪些資源,資源大小,
          加載資源所消耗的時間

          四、超鏈接
          1.簡介
          使用超鏈接可以從一個頁面跳轉到另外一個頁面,實現頁面之間導航
          當鼠標移動到超鏈接文本或圖片時,鼠標箭頭會變成一只小手
          超鏈接有三種類型:
          普通鏈接/頁面間的鏈接,跳轉到另一個頁面 錨鏈接:鏈接到錨點(鏈接到同一個頁面的指定位置) 功能鏈接:實現特殊功能(發郵件,下載)
          2.基本用法
          使用 標簽來創建超鏈接
          語法格式:

          常用屬性:
          href:鏈接地址或路徑,鏈接地址

          world

          鏈接文本或圖片

          1 2 3 4 5 1 target:鏈接打開的位置,取值

          路徑分類:
          絕對路徑 以根開始的路徑
          file:///D:/software/b.html https://www.baidu.com/img/bd_logo1.png
          相對路徑 相對于當前頁面文件所在的路徑,不是以根開始的路徑 ./ 當前路徑 …/ 當前位置上一級目錄

          3.錨鏈接
          3.1簡介
          點擊鏈接后跳轉到某一個頁面的指定位置(錨點anchor)
          錨鏈接的分類:
          頁面內的錨鏈接 頁面間的錨鏈接
          3.2 頁面內的錨鏈接
          步驟:

          1. 定義錨點(標記)
          2. 鏈接錨點
            _self 自身,當前,默認值 _blank新的,空白的 _parent父層框架 _top頂層框架
            目標位置
            1 2 3 4
            1

          3.3 頁面間的錨鏈接

          4.功能鏈接
          5.URL
          5.1 簡介
          URL:Uniform Resource Locator 統一資源定位器,用來定位資源所在的位置,最常見的就是網址

          5.2 組成
          一個完整的URL由8個部分組成:
          協議:prococol 如 http:超文本傳輸協議,用來訪問WEB網站Hyper text Transfer protocal https:更加安全的協議 SSL安全套接子層 ftp文件傳輸協議,用來訪問服務器上的文件,實現文件的上傳和下載File Transfer protocol file:文件協議,用來訪問本地文件 主機名hostname服務器地址或服務器Netbios名稱,如www.baidu.com ftp://10.255.254.254 端口:port位于主機名的后面,使用冒號進行分隔 不同的協議使用不同的端口,如http使用80端口,https使用的443端口,ftp使用20和21 如果使用的是默認端口,則端口可以省略 如果使用的不是默認端口,則必須指定端口http://59.49.32.213:7070/ 路徑:path目標文件所在的路徑結構,如:www.baidu.com/img/ 資源resource要訪問的目標文件,如bd_logo1.png 查詢字符串:query string 也稱為參數 在資源后面使用?開頭的一組名稱/值
          鏈接文本
          鏈接文本
          https://www.baidu.com/img/bd_logo1.png?name=tom&age=2&sex=male https://www.w3school.com.cn/html/html_quotation_elements.asp file:///C:/Users/Administrator/Desktop/project/code/09.%E5%B8%B8%E7%94%A8%E6%A0%87%E7%A D%BE3.html http://www.sxgjpx.net/ ftp://10.255.254.253/
          1
          1
          1 2 3
          4 5
          名稱和值之間以=分隔,多個之間用&分隔,如:name=tom&age=2&sex=male 錨點anchor,在資源后面使用#開頭的文本,如#6 身份認證authentication,指定身份信息,如:ftp://賬戶:密碼@ftp.bbshh010.com
          五、表格
          1.簡介
          表格是一個規則的行列結構,每個表格是由若干行組成,每行由若干個單元格組成
          table row column
          2.基本結構
          2.1 table標簽
          用來定義表格
          常用屬性:
          border:表格邊框 默認為0 width/height:寬度/高度 bordercolor:邊框的顏色 align:對齊方式,取值:left(默認) center居中 right居右 bgcolor:背景顏色 background:背景圖片 cellspacing間距:單元格與單元格之間的距離 cellpadding邊距:單元格中的內容到邊界之間的距離
          2.2 tr標簽
          用來定義行:table row
          常用屬性:
          align:水平對齊 取值:left(默認) center right valign垂直對齊 取值:top center bottom bgcolor:背景顏色 background:背景圖片
          2.3 td標簽
          用來定義單元格,table data
          常用屬性:align、valign、bgcolor、background
          注意:表格必須是由行組成,行必須由單元格來組成,數據必須放到單元格中
          3.合并單元格
          合并單元格也稱為單元格的跨行跨列
          兩個屬性:
          rowspan 設置單元格所跨的行數 colspan 設置單元格所跨的列數
          步驟:

          1. 在跨越的單元格中設置rowspan/colspan屬性 2. 將被跨越的單元格刪除
            必須要保證每行的實際列數是相同的,否則表格可能會出錯亂
            4.高級標簽
            4.1caption標簽
            表格的標題標簽
            4.2thead標簽
            表格的頭部table head
            4.3th標簽
            表格的頭部標題table head title
            一般用在thead中,設置頭部的標題,替代td標簽,與td的區別,th中的文本會加粗且居中顯示
            4.4tbody標簽
            表格的主體table body
            4.5tfoot標簽
            表格的底部table foot




          六、表單
          1.簡介
          表單是一個包含若干個表單元素的區域,用于獲取瑣類型的用戶數據

          表單元素是允許用戶在表單輸入信息的元素,如文本框、密碼框、單選按鈕、復選框、下拉列表、按鈕等
          2.表單結構
          2.1表單語法

          1

          2.2form標簽
          用來定義表單,可以包含多個表單元素
          常用屬性:
          action:提交數據給誰處理,即處理數據的程序,默認為當前頁面 method:提交數據的方式或方法,取值:get(默認),post get和post的區別: get:以查詢字符串的形式提交,在地址欄中能看到,長度有限制,不安全 post以表單數據組的形式進行提交,在地址欄中看不到,長度無限制,安全 enctype(encode type)編碼類型:提交數據的編碼,取值:application/X-www-form-urlencoded(默 認)、multipart/form-data(文件上傳)
          3.表單元素
          大多數的表單元素都是使用 標簽來定義的,通過設置屬性type來定義不同的表單元素

          1

          3.1單行文本框
          常用屬性:
          ·name名稱,很重要,如果沒有定義name屬性,則該表單元素的數據是無法提交的

          ·value初始值

          ·size顯示寬度

          ·maxlength:大字符數,默認是沒有限制

          ·readonly只讀:readonly=“readonly”,可簡寫readonly,即只寫屬性名

          ·disabled禁用:disabled=“disabled”, 可簡寫disabled完全禁用

          表單元素被提交的兩個條件,1.有name屬性2.非disabled

          3.2 單選按鈕
          常用屬性:

          ·name名稱:多個radio的name屬性必須相同,才能實現互斥(單選)

          ·value值

          ·checked:是否被選中,兩種狀態,選中,未選中 checked=“checked” 簡寫 checked

          3.3 復選框
          常用屬性與單選按鈕radio類似

          3.4 文件選擇器
          常用屬性:

          ·name:名稱

          ·accept設置可選擇的文件類型,用來限制上傳的文件類型

          使用MIME格式字符串對資源類型進行限制

          常見的MIME類型:

          ·純文本:text/plain text/xml text/html

          · 圖像:image/png image/jpeg image/gif

          4.特殊表單元素

          4.1下拉列表

          select常用屬性:

          ·name名稱

          ·size行數,同時顯示多個選項

          ·multiple允許同時選擇多個

          option常用屬性:

          ·value選項值

          ·selected設置默認選中項

          optgroup常用屬性:

          ·label分組的標簽

          4.2文本域

          ·name名稱

          ·rows行數

          ·cols列數


          5、其他標簽

          5.1 label標簽

          為表單元素提供標簽,當選中label標簽中的文本內容時會自動將光標切換到與之相關聯的表單元素。

          常用屬性:

          ·for必須將該屬性值設置為與相關聯的表單元素的Id屬性值相同。

          注:幾乎所有HTML標簽都具有id屬性,且id值必須唯一。

          5.2 button標簽

          也表示按鈕,與input按鈕類似

          語法:

          1按鈕文字或圖像

          常用屬性:

          ·type按鈕的類型,取值: submit(默認)、reset、button

          5.3 fieldset和legend標簽

          fieldset標簽,對表單元素進行分組

          legend標簽,對分組添加標題

          七、內嵌框架
          1、簡介

          使用iframe可以在一個頁面中引用另一個頁面,實現復用、靈活

          2、基本用法

          語法:

          1

          常用屬性:

          · src:引用的頁面

          · width/height寬度/高度 ,像素或百分比

          · frameborder是否顯示邊框,取值:1(yes) 0(no)—默認

          · scrolling是否顯示滾動條,取值:yes no auto

          · name屬性 為框架定義名稱

          3、在框架中打開鏈接

          1

          2

          3鏈接的文本或圖像

          八、HTML5簡介

          1、發展

          W3C于1992年12月發布了HTML4.0.1標準
          W3C于2014年10月發布了HTML5標準

          2、特點

          · 取消了過時的標簽,如font、center等,它們僅具有展示外觀的功能

          · 增加了一些更具有語義化的標簽,如header、footer、aside等

          · 增加了一些新功能標簽,如canvas、audio、video

          · 增加了一些表單控件,如email、date、time、url、search等

          · 可以直接在瀏覽器中繪畫(canvas),無需flash

          · 增加了本地存儲的支持

          3、兼容性

          http://caniuse.com

          提供了各種瀏覽器版本對HTML5和CSS規范的支持度

          九、HTML5新增內容
          1、結構相關的標簽

          用來進行頁面結構布局,本身無任何特殊樣式,需要使用CSS進行樣式設置

          · article定義一個獨立的內容,完整的文章

          · section定義文檔的章節、段落

          · header文章的頭部、頁眉、標題

          · footer文章的底部、頁腳、標注

          · aside定義側邊欄

          · figure圖片區域

          · figcaption為圖片區域定義標題

          · nav定義導航菜單

          結構標簽只是表明各部分的角色,并無實際的外觀樣式,與普通div相同

          2、語義相關的標簽
          2.1 mark標簽
          標注,用來突出顯示文本,默認添加黃色背景
          2.2 time標簽
          定義日期和時間,便于搜索引擎智能查找
          2.3 details和 summary標簽
          默認顯示summary中的內容,點擊后顯示details中的內容
          注:并不是所有的瀏覽器都兼容,chrome、opera支持、Firefox、IE瀏覽器不支持
          2.4 meter標簽
          計數儀,表示度量
          常用屬性:

          · max定義大值,默認為1

          · min定義小值,默認為0

          · value定義當前值

          · high定義限定為高的值

          · low定義限定為低的值

          · optimum定義佳值

          規則:

          1. 如果optimum大于high,則表示值越大越好

          當value大于high時為綠色

          當value在low與high之間時為黃色

          當value小于low時為紅色

          1. 如果optimum小于low,則表示值越小越好

          當value小于low時為綠色

          當value在low與high之間時為黃色

          當value大于high時為紅色

          1. 當optimum介于low和high之間,則表示值在low和high之間好當value在low與high之間時顯示綠色,否則顯示黃色

          2.5 progress標簽
          進度條,表示運行中的進度
          常用屬性:

          · value定義當前值

          · max定義完成的值

          3.表單相關
          3.1 新增表單元素
          新增以下type類型:

          · email接收郵箱

          · url接收URL

          · tel接收電話號碼,目前僅在移動設備上有效

          · search搜索文框

          · number/range接收數字/數字滑塊,包含min,max,step屬性

          · date/month/week/time/datetime日期時間選擇器,兼容性不好

          · color顏色拾取

          作用:

          · 具有格式校驗的功能

          · 可以與移動設備的鍵盤相關聯

          3.2新增表單屬性
          form標簽的屬性:

          · autocomplete是否啟動表單的自動完成功能, 取值:on(默認)、o?

          · novalidate提交表單時不進行校驗,默認會進行表單校驗

          3.3 新增表單元素的屬性
          新增表單元素屬性:input/select/textarea等

          · placeholder提示文字

          · required是否必填

          · autocomplete是否啟用該表單元素的自動完成功能

          · autofocus設置初始焦點元素

          · pattern使用正則表達式(RegExp后面會講解),進行數據校驗

          · list使文本元素具有下拉列表的功能,需要配合datalist和option標簽一起使用

          · form可以將表單元素寫在form標簽外面,然后通過該屬性關聯指定的表單

          4、多媒體標簽
          4.1audio標簽
          在頁面中插入音頻,不同的瀏覽器對音頻格式的支持不一樣
          audio常用屬性:

          · src音頻文件的來源

          · controls是否顯示控制面板,默認不顯示

          · autoplay是否自動播放,默認不自動播放

          · loop是否循環播放

          · muted是否靜音

          · preload是否預加載,取值:none不預加載、auto預加載(默認)、metadata只加載元數據

          如果設置了autoplay屬性,則該屬性無效

          可以結合source標簽使用,指定多個音頻文,瀏覽器會檢測并使用第一個可用的音頻文件

          4.2 video標簽
          在頁面中插入視頻,不同的瀏覽器對視頻格式的支持不一樣
          用法與audio標簽基本相同,增加屬性:

          · widht/height視頻播放器的寬度/高度

          · poster在視頻加載前顯示的圖片

          案例1hello.html

          <html>
          	<body>
          		<tiele>HTML技術</tiele>
          	</body>
          	<body>
          		大家好,歡迎學習html技術!
          	</body>
          </html>1234567

          效果

          [外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-h271e4v6-1593240920352)(C:\Users\lenovo\Desktop\新建文件夾\靜態網頁2\案例\result\案例1.png)]

          案例2標簽的組成.html

          <!DOCTYPE html>
          <html lang="en">
          <head>
          	<meta charset="UTF-8">
          	<title>標簽</title>
          </head>
          <body text="blue">
          	標簽的組成
          	<br>
          	html從入門到精通!
          	<hr>
          	<h1>標簽的分類</h1>
          	<hr>
          
          	<h2>標簽的分類</h2>
          	<hr>
          
          	<h6>標簽的分類</h6>
          	<hr>
          
          	<span>哈哈</span>嘿嘿
          	
          </body>
          </html>1234567891011121314151617181920212223

          效果

          [外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-jx6zJE1P-1593240920354)(C:\Users\lenovo\Desktop\新建文件夾\靜態網頁2\案例\result\案例2.png)]

          案例3實體字符.html

          <!DOCTYPE html>
          <html lang="en">
          <head>
          	<meta charset="UTF-8">
          	<title>Document</title>
          </head>
          <body>
          	圖書:<<HTML從入門到精通<<
          	<hr>
          
          	北京      上海      廣州
          	<hr>
          
          	在HTML中用<表示<小于號
          	<hr>
          
          	“HTML語言” 或 &qout;HTML語言&qout;
          	<hr>
          
          	版權所有? 2000-2020 高教培訓
          	<hr>
          
          	×關閉符號
          </body>
          </html>123456789101112131415161718192021222324

          效果

          [外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-nuFLl3hm-1593240920355)(C:\Users\lenovo\Desktop\新建文件夾\靜態網頁2\案例\result\案例3.png)]

          (剩下的下期出)

          原文鏈接:https://blog.csdn.net/WanXuang/article/details/106982782?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522160513384519724835852804%2522%252C%2522scm%2522%253A%252220140713.130102334..%2522%257D&request_id=160513384519724835852804&biz_id=&utm_medium=distribute.pc_search_top_result.none-task-code-2~all~top_position~default-1-106982782-12.nonecase&utm_term=html

          作者:WanXuang

          出處:從CSDN


          主站蜘蛛池模板: 一区二区三区日韩| 中文字幕在线一区二区在线| 国产精品一区二区毛卡片| 亚洲天堂一区在线| 日韩精品一区二区三区中文精品| 无码精品国产一区二区三区免费| 国偷自产av一区二区三区| 无码精品人妻一区二区三区漫画 | 日韩免费观看一区| 一区二区不卡在线| 亚洲无线码在线一区观看| 久久er99热精品一区二区 | 国产人妖视频一区二区破除| 国产综合无码一区二区辣椒| 午夜性色一区二区三区不卡视频 | 日本一区二区三区精品国产 | 视频一区视频二区日韩专区| 美女视频免费看一区二区| 污污内射在线观看一区二区少妇| 日日摸夜夜添一区| 国产综合无码一区二区三区| 在线一区二区观看| 国产精品电影一区二区三区| 亚洲AV无码片一区二区三区| 亚洲人AV永久一区二区三区久久| 精品深夜AV无码一区二区| 久久久久人妻一区精品 | 亚洲av乱码一区二区三区| 无码人妻一区二区三区免费看| 日本精品高清一区二区| 亚洲一区电影在线观看| 亚洲美女高清一区二区三区| 亚洲欧洲一区二区三区| 肥臀熟女一区二区三区| 嫩B人妻精品一区二区三区| 亚洲综合无码AV一区二区| 亚洲V无码一区二区三区四区观看| 久久亚洲国产精品一区二区| 亚洲一区二区影院| 国产a∨精品一区二区三区不卡| 亚洲综合无码AV一区二区|