製作一個 Youtube 影片搜尋網頁 成品:
成品網址
成品功能: 搜尋欄位可以輸入內容並且有 Search 按鈕 把找到符合輸入內容的影片五篇呈現在下方 下一頁的按鈕如果還有更多需要內容呈現 上一頁的按鈕可以回到前五篇的內容 點擊超連結(圖片以及標題)會在當前頁面直接跳出影片視窗並且有關閉按鈕(iframe) HTML html 程式碼: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 <!DOCTYPE html > <html lang ="en" > <head > <title > YouTube Search Engine</title > <link rel ="stylesheet" href ="style.css" /> <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/fancybox/3.5.7/jquery.fancybox.min.css" /> </head > <body > <nav class ="navbar" > <h1 > <a href ="https://www.youtube.com/" > <span > YouTube</span > </a > Search Engine </h1 > </nav > <div class ="container" > <section class ="search" > <input id ="inputval" type ="text" placeholder ="Search..." /> <button id ="btngetval" class ="search-btn btn" > Search</button > </section > <div class ="results" > </div > <div class ="buttons" > </div > </div > <footer class ="footer" > <p > Copyright © <span id ="year" > </span > , All Rights Reserved</p > </footer > <script src ="https://code.jquery.com/jquery-3.5.1.min.js" > </script > <script src ="script.js" > </script > <script > $("#year" ).text(new Date ().getFullYear()); </script > <script src ="https://cdnjs.cloudflare.com/ajax/libs/fancybox/3.5.7/jquery.fancybox.min.js" > </script > <script > $("[data-fancybox]" ).fancybox({ toolbar: false , smallBtn: true , iframe: { preload: false , }, }); </script > </body > </html >
CSS: CSS 完整程式碼 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 @import url("https://fonts.googleapis.com/css2?family=Lato:ital,wght@0,100;0,300;0,400;0,700;0,900;1,100;1,300;1,400;1,700;1,900&display=swap" );* { margin : 0 ; padding : 0 ; box-sizing : border-box; } body { display : flex; flex-direction : column; justify-content : end; align-items : center; height : 100vh ; background-color : #1d1d1d ; color : #dedede ; font-family : "Lato" , sans-serif; margin : 0 ; position : relative; width : 100% ; } * span { color : #06c5a9 ; position : relative; } nav h1 { font-weight : bold; } a { color : #06c5a9 ; text-decoration : none; } .navbar { margin : 20px ; } section { display : flex; margin : 10px 0 10px 0 ; } .container { padding : 60px ; margin : 40px ; max-width : 700px ; box-shadow : 0 3px 10px rgba (255 , 255 , 255 , 1 ); background-color : rgb (0 , 0 , 0 ); display : flex; flex-direction : column; justify-content : center; align-items : stretch; border-radius : 5px ; height : auto; width : auto; } .container .search { display : flex; justify-content : center; } section input { outline : none; box-shadow : 0 3px 10px rgba (255 , 255 , 255 , 1 ); border-radius : 5px ; } img { position : relative; margin : 10px ; margin-right : 20px ; width : 148px ; height : 113px ; padding : 3px ; border-radius : 5px ; border : solid 1px rgb (58 , 57 , 57 ); box-shadow : 0 3px 10px rgba (255 , 255 , 255 , 1 ); } * p { overflow : auto; } .text { padding : 3px ; margin : 10px ; } section .text { font-size : 16px ; word-break : break-all; } * .btn { padding : 5px ; margin : 5px ; font-size : 14px ; border-radius : 5px ; background : #444451 ; outline : none; color : #dedede ; box-shadow : 0 3px 10px rgba (255 , 255 , 255 , 0.7 ); } .page { margin-bottom : 30px ; } footer { color : #31312f ; } footer span { color : #31312f ; }
小補充:
無
JS: 取得 Youtube API 可以操作使用的 url 參考網址: Youtube Data API - Search Engine
可以從這邊設定你想要添加的屬性並且操作看看會取得的網址內容且不會消耗配額Data API 官方文件 - Try this API
針對這個專案我選用: part: snippet(這個基本上就是我們需要的)
這邊不要勾 google oauth2.0
基本上就可以符合這個專案的需求瞜!
url 回傳資料操作 需要操作的屬性標註在下方圖片中把他們貼到 DOM 就可以搂!
變數設置 nextPageToken - 是換頁最重要的內容必須嵌入網址中 prevPageToken - 同上 q - 所有 function 都必須抓取搜尋欄的內容並且必須一致 output - 把 DOM 的內容貼進去再去做append()
,html()
DOM 的變數設置 - 處理資料並且針對屬性去做抓取的動作 functions: getVideoData(q) -功能:呈現搜尋內容 使用.ajax()
的 GET 方法取得 url 回傳的資料(url 部分必須嵌入參數讓其可以動態改變)
注意:
datatype:必須填入 json 網址後方的 id key 要記得填入 1 2 3 4 5 $.ajax({ type: "GET" , url: ` https://youtube.googleapis.com/youtube/v3/search?part=snippet&channelType=any&order=relevance&q=${q} &type=video&videoCaption=any&videoEmbeddable=any&videoLicense=any&videoType=any&prettyPrint=true&key=AIzaSyDNdqNoZCYqxEJ0nHKh3BWO7Yxc7fLLH2I` , dataType: "json" ,
下一步在.ajax()
屬性sucess
中把回傳的資料處理並且forEach
上去 DOM 上面
最後處理按鈕:
所有的頁面回傳資料都會包含上、下一頁的 token(如果有的話,沒有會回傳 undefined),
跟getVideoData(q)
功能幾乎一樣,但是因為是下一頁所以必須以動態的方式抓取 token 以及 q, 頁面的 token 已經被傳入按鈕中,所以使用.data()
的方式抓取資料(下方有補充資料)並且傳入 url 中,並且一樣會產出頁面 token(因為一樣得輸出上下頁按鈕)並且輸入到getBtn(nextPageToken, prevPageToken)
讓下一頁可以吃到 token
1 2 3 4 5 $.ajax({ type: "GET" , url: ` https://youtube.googleapis.com/youtube/v3/search?part=snippet&channelType=any&order=relevance&pageToken=${nextPageToken} &q=${q} &type=video&videoCaption=any&videoEmbeddable=any&videoLicense=any&videoType=any&prettyPrint=true&key=AIzaSyDNdqNoZCYqxEJ0nHKh3BWO7Yxc7fLLH2I` , dataType: "json" ,
功能內容同上,只需要更改參數名稱
getBtn(nextPageToken, prevPageToken) - 功能:製造按鈕 按鈕傳遞上一頁/下一頁的 token 使用判斷是來決定呈現什麼按鈕 一樣必須定義 q 是搜尋欄輸入的內容 使用onclick
把換頁以及搜尋參數傳入nextPage()
,prevPage()
function 內 事件監聽 功能:取得 input 的值並且輸入 getVideoData(q)內
1 2 3 4 5 6 $("#btngetval" ).click(function (e ) { e.preventDefault(); let q = $("#inputval" ).val(); getVideoData(q); });
iframe 使用 html 引入 iframe iframe 引用網址 iframe 官方文件
最上方引入 iframe 的 CSS 設定一定在官網可以找到
1 2 3 4 5 <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/fancybox/3.5.7/jquery.fancybox.min.css" />
直接擺入 html 最下方來引入(跟 jQuery 或是 js 檔案引入位置相同)
1 2 3 4 5 6 7 8 9 10 11 12 13 <script src ="https://cdnjs.cloudflare.com/ajax/libs/fancybox/3.5.7/jquery.fancybox.min.js" > </script > <script > $("[data-fancybox]" ).fancybox({ toolbar: false , smallBtn: true , iframe: { preload: false , }, }); </script >
針對頁面超連結的部份 最後面的部份是 videoId 可以在 id 內找到(這邊是 sucess 後回傳的資料)
使用這串就可以把它們嵌入 iframe 內https://www.youtube.com/embed/${vid.id.videoId}
並且按照這個模式操作 iframe 的區塊
1 <a data-fancybox data-type="iframe" data-src="https://www.youtube.com/embed/${vid.id.videoId}" href="javascript:;" ></a>
小補充 .data() - 官方文件
.ajax() - 官方文件
JS 完整程式碼: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 $("#btngetval" ).click(function (e ) { e.preventDefault(); let q = $("#inputval" ).val(); getVideoData(q); }); function getVideoData (q ) { $.ajax({ type: "GET" , url: ` https://youtube.googleapis.com/youtube/v3/search?part=snippet&channelType=any&order=relevance&q=${q} &type=video&videoCaption=any&videoEmbeddable=any&videoLicense=any&videoType=any&prettyPrint=true&key=AIzaSyDNdqNoZCYqxEJ0nHKh3BWO7Yxc7fLLH2I` , dataType: "json" , success: function (res ) { let videos = res.items; let nextPageToken = res.nextPageToken; let prevPageToken = res.prevPageToken; let output = "" ; [...videos].forEach(function (vid ) { output += `<section class="video-area"> <div class="img"> <a data-fancybox data-type="iframe" data-src="https://www.youtube.com/embed/${vid.id.videoId} " href="javascript:;" ><img src="${vid.snippet.thumbnails.default .url} " /></a> </div> <div class="text"> <a data-fancybox data-type="iframe" data-src="https://www.youtube.com/embed/${vid.id.videoId} " href="javascript:;" ><h3>${vid.snippet.title} </h3></a ><small>By <span> ${vid.snippet.channelTitle} </span> on ${vid.snippet.publishedAt} </small> <p>${vid.snippet.description} </p> </div> </section> ` ; $(".results" ).html(output); let btn = getBtn(nextPageToken, prevPageToken); $(".buttons" ).html(btn); }); }, }); } function nextPage ( ) { let nextPageToken = $("#next-button" ).data("token" ); let q = $("#next-button" ).data("query" ); q = $("#inputval" ).val(); $(".results" ).html("" ); $(".buttons" ).html("" ); $.ajax({ type: "GET" , url: ` https://youtube.googleapis.com/youtube/v3/search?part=snippet&channelType=any&order=relevance&pageToken=${nextPageToken} &q=${q} &type=video&videoCaption=any&videoEmbeddable=any&videoLicense=any&videoType=any&prettyPrint=true&key=AIzaSyDNdqNoZCYqxEJ0nHKh3BWO7Yxc7fLLH2I` , dataType: "json" , success: function (data ) { let nextPageToken = data.nextPageToken; let prevPageToken = data.prevPageToken; let res = data.items; let output = "" ; [...res].forEach(function (vid ) { output += `<section class="video-area"> <div class="img"> <a data-fancybox data-type="iframe" data-src="https://www.youtube.com/embed/${vid.id.videoId} " href="javascript:;" ><img src="${vid.snippet.thumbnails.default .url} " /></a> </div> <div class="text"> <a data-fancybox data-type="iframe" data-src="https://www.youtube.com/embed/${vid.id.videoId} " href="javascript:;" ><h3>${vid.snippet.title} </h3></a ><small>By <span> ${vid.snippet.channelTitle} </span> on ${vid.snippet.publishedAt} </small> <p>${vid.snippet.description} </p> </div> </section> ` ; let btn = getBtn(nextPageToken, prevPageToken); $(".results" ).html(output); $(".buttons" ).html(btn); }); }, }); } function prevPage ( ) { let prevPageToken = $("#prev-button" ).data("token" ); let q = $("#next-button" ).data("query" ); q = $("#inputval" ).val(); $(".results" ).html("" ); $(".buttons" ).html("" ); $.ajax({ type: "GET" , url: ` https://youtube.googleapis.com/youtube/v3/search?part=snippet&channelType=any&order=relevance&pageToken=${prevPageToken} &q=${q} &type=video&videoCaption=any&videoEmbeddable=any&videoLicense=any&videoType=any&prettyPrint=true&key=AIzaSyDNdqNoZCYqxEJ0nHKh3BWO7Yxc7fLLH2I` , dataType: "json" , success: function (data ) { let nextPageToken = data.nextPageToken; let prevPageToken = data.prevPageToken; let res = data.items; let output = "" ; [...res].forEach(function (vid ) { output += `<section class="video-area"> <div class="img"> <a data-fancybox data-type="iframe" data-src="https://www.youtube.com/embed/${vid.id.videoId} " href="javascript:;" ><img src="${vid.snippet.thumbnails.default .url} " /></a> </div> <div class="text"> <a data-fancybox data-type="iframe" data-src="https://www.youtube.com/embed/${vid.id.videoId} " href="javascript:;" ><h3>${vid.snippet.title} </h3></a ><small>By <span> ${vid.snippet.channelTitle} </span> on ${vid.snippet.publishedAt} </small> <p>${vid.snippet.description} </p> </div> </section> ` ; let btn = getBtn(nextPageToken, prevPageToken); $(".results" ).html(output); $(".buttons" ).html(btn); }); }, }); } function getBtn (nextPageToken, prevPageToken ) { let q = $("#inputval" ).val(); if (nextPageToken === undefined ) { $(".buttons" ).html(); return ; } else if (prevPageToken === undefined ) { $(".buttons" ).html( `<div class="button-container"><button id="next-button" class="btn paging-button" data-token="${nextPageToken} " data-query="${q} " onclick="nextPage();">Next Page</button></div>` ); return ; } else { $(".buttons" ).html( `<div class="button-container"><button id="prev-button" class="btn paging-button" data-token="${prevPageToken} " data-query="${q} " onclick="prevPage();">Prev Page</button></div> <div class="button-container"><button id="next-button" class="btn paging-button" data-token="${nextPageToken} " data-query="${q} " onclick="nextPage();">Next Page</button></div> ` ); } }