加載圖片是提高用戶體驗的一個很好方法。圖片預(yù)先加載到瀏覽器中,訪問者便可順利地在你的網(wǎng)站上沖浪,并享受到極快的加載速度。
這對圖片畫廊及圖片占據(jù)很大比例的網(wǎng)站來說十分有利,它保證了圖片快速、無縫地發(fā)布,也可幫助用戶在瀏覽你網(wǎng)站內(nèi)容時獲得更好的用戶體驗。本文將分享三個不同的預(yù)加載技術(shù),來增強網(wǎng)站的性能與可用性。
方法一:用css和JavaScript實現(xiàn)預(yù)加載
實現(xiàn)預(yù)加載圖片有很多方法,包括使用css、JavaScript及兩者的各種組合。這些技術(shù)可根據(jù)不同設(shè)計場景設(shè)計出相應(yīng)的解決方案,十分高效。
單純使用CSS,可容易、高效地預(yù)加載圖片,代碼如下:
#preload-01 { background: url(http://domain.tld/image-01.png) no-repeat -9999px -9999px; }
#preload-02 { background: url(http://domain.tld/image-02.png) no-repeat -9999px -9999px; }
#preload-03 { background: url(http://domain.tld/image-03.png) no-repeat -9999px -9999px; }
將這三個ID選擇器應(yīng)用到(X)html元素中,我們便可通過CSS的background屬性將圖片預(yù)加載到屏幕外的背景上。
只要這些圖片的路徑保持不變,當(dāng)它們在Web頁面的其他地方被調(diào)用時,瀏覽器就會在渲染過程中使用預(yù)加載(緩存)的圖片。簡單、高效,不需要任何JavaScript。
該方法雖然高效,但仍有改進余地。使用該法加載的圖片會同頁面的其他內(nèi)容一起加載,增加了頁面的整體加載時間。
為了解決這個問題,我們增加了一些JavaScript代碼,來推遲預(yù)加載的時間,直到頁面加載完畢。代碼如下:
function preloader() {
if (document.getElementById) {
document.getElementById("preload-01").style.background = "url(http://domain.tld/image-01.png) no-repeat -9999px -9999px";
document.getElementById("preload-02").style.background = "url(http://domain.tld/image-02.png) no-repeat -9999px -9999px";
document.getElementById("preload-03").style.background = "url(http://domain.tld/image-03.png) no-repeat -9999px -9999px";
}
}
function addLoadEvent(func) {
var oldonload = window.onload;
if (typeof window.onload != 'function') {
window.onload = func;
} else {
window.onload = function() {
if (oldonload) {
oldonload();
}
func();
}
}
}
addLoadEvent(preloader);
在該腳本的第一部分,我們獲取使用類選擇器的元素,并為其設(shè)置了background屬性,以預(yù)加載不同的圖片。
該腳本的第二部分,我們使用addLoadEvent()函數(shù)來延遲preloader()函數(shù)的加載時間,直到頁面加載完畢。
如果JavaScript無法在用戶的瀏覽器中正常運行,會發(fā)生什么?很簡單,圖片不會被預(yù)加載,當(dāng)頁面調(diào)用圖片時,正常顯示即可。
上述方法有時確實很高效,但我們逐漸發(fā)現(xiàn)它在實際實現(xiàn)過程中會耗費太多時間。相反,我更喜歡使用純JavaScript來實現(xiàn)圖片的預(yù)加載。
下面將提供兩種這樣的預(yù)加載方法,它們可以很漂亮地工作于所有現(xiàn)代瀏覽器之上。
JavaScript代碼段1
只需簡單編輯、加載所需要圖片的路徑與名稱即可,很容易實現(xiàn):
<div class="hidden">
<script type="text/javascript">
var images = new Array()
function preload() {
for (i = 0; i < preload.arguments.length; i++) {
images[i] = new Image()
images[i].src = preload.arguments[i]
}
}
preload(
"http://domain.tld/gallery/image-001.jpg",
"http://domain.tld/gallery/image-002.jpg",
"http://domain.tld/gallery/image-003.jpg"
)
</script>
</div>
該方法尤其適用預(yù)加載大量的圖片。我的畫廊網(wǎng)站使用該技術(shù),預(yù)加載圖片數(shù)量達50多張。將該腳本應(yīng)用到登錄頁面,只要用戶輸入登錄帳號,大部分畫廊圖片將被預(yù)加載。
JavaScript代碼段2
該方法與上面的方法類似,也可以預(yù)加載任意數(shù)量的圖片。將下面的腳本添加入任何Web頁中,根據(jù)程序指令進行編輯即可。
<div class="hidden">
<script type="text/javascript">
if (document.images) {
img1 = new Image();
img2 = new Image();
img3 = new Image();
img1.src = "http://domain.tld/path/to/image-001.gif";
img2.src = "http://domain.tld/path/to/image-002.gif";
img3.src = "http://domain.tld/path/to/image-003.gif";
}
</script>
</div>
正如所看見,每加載一個圖片都需要創(chuàng)建一個變量,如“img1 = new Image();”,及圖片源地址聲明,如“img3.src = “../path/to/image-003.gif”;”。參考該模式,你可根據(jù)需要加載任意多的圖片。
我們又對該方法進行了改進。將該腳本封裝入一個函數(shù)中,并使用 addLoadEvent(),延遲預(yù)加載時間,直到頁面加載完畢。
function preloader() {
if (document.images) {
var img1 = new Image();
var img2 = new Image();
var img3 = new Image();
img1.src = "http://domain.tld/path/to/image-001.gif";
img2.src = "http://domain.tld/path/to/image-002.gif";
img3.src = "http://domain.tld/path/to/image-003.gif";
}
}
function addLoadEvent(func) {
var oldonload = window.onload;
if (typeof window.onload != 'function') {
window.onload = func;
} else {
window.onload = function() {
if (oldonload) {
oldonload();
}
func();
}
}
}
addLoadEvent(preloader);
上面所給出的方法似乎不夠酷,那現(xiàn)在來看一個使用Ajax實現(xiàn)圖片預(yù)加載的方法。該方法利用DOM,不僅僅預(yù)加載圖片,還會預(yù)加載CSS、JavaScript等相關(guān)的東西。使用Ajax,比直接使用JavaScript,優(yōu)越之處在于JavaScript和CSS的加載不會影響到當(dāng)前頁面。該方法簡潔、高效。
window.onload = function() {
setTimeout(function() {
// XHR to request a js and a CSS
var xhr = new XMLHttpRequest();
xhr.open('GET', 'http://domain.tld/preload.js');
xhr.send('');
xhr = new XMLHttpRequest();
xhr.open('GET', 'http://domain.tld/preload.css');
xhr.send('');
// preload image
new Image().src = "http://domain.tld/preload.png";
}, 1000);
};
上面代碼預(yù)加載了“preload.js”、“preload.css”和“preload.png”。1000毫秒的超時是為了防止腳本掛起,而導(dǎo)致正常頁面出現(xiàn)功能問題。
下面,我們看看如何用JavaScript來實現(xiàn)該加載過程:
window.onload = function() {
setTimeout(function() {
// reference to <head>
var head = document.getElementsByTagName('head')[0];
// a new CSS
var css = document.createElement('link');
css.type = "text/css";
css.rel = "stylesheet";
css.href = "http://domain.tld/preload.css";
// a new JS
var js = document.createElement("script");
js.type = "text/javascript";
js.src = "http://domain.tld/preload.js";
// preload JS and CSS
head.appendChild(css);
head.appendChild(js);
// preload image
new Image().src = "http://domain.tld/preload.png";
}, 1000);
};
這里,我們通過DOM創(chuàng)建三個元素來實現(xiàn)三個文件的預(yù)加載。正如上面提到的那樣,使用Ajax,加載文件不會應(yīng)用到加載頁面上。從這點上看,Ajax方法優(yōu)越于JavaScript。
- End -
tml2canvas
簡介
我們可以直接在瀏覽器端使用html2canvas,對整個或局部頁面進行‘截圖’。但這并不是真的截圖,而是通過遍歷頁面DOM結(jié)構(gòu),收集所有元素信息及相應(yīng)樣式,渲染出canvas image。
由于html2canvas只能將它能處理的生成canvas image,因此渲染出來的結(jié)果并不是100%與原來一致。但它不需要服務(wù)器參與,整個圖片都由客戶端瀏覽器生成,使用很方便。
使用
使用的API也很簡潔,下面代碼可以將某個元素渲染成canvas:
html2canvas(element, { onrendered: function(canvas) { // canvas is the final rendered <canvas> element } });
通過onrendered方法,可以將生成的canvas進行回調(diào),比如插入到頁面中:
html2canvas(element, { onrendered: function(canvas) { document.body.appendChild(canvas); } });
做個小例子代碼如下,在線展示鏈接demo1
<html> <head> <title>html2canvas example</title> <style type="text/css">...</style> </head> <body> <header> <nav> <ul> <li>one</li> ... </ul> </nav> </header> <section> <aside> <h3>it is a title</h3> <a href="">Stone Giant</a> ... </aside> <article> <img src="./Stone.png"> <h2>Stone Giant</h2> <p>Coming ... </p> <p>以一團石頭...</p> </article> </section> <footer>write by linwalker @2017</footer> <script type="text/javascript" src="./html2canvas.js"></script> <script type="text/javascript"> html2canvas(document.body, { onrendered:function(canvas) { document.body.appendChild(canvas) } }) </script> </body> </html>
這個例子將頁面body中的元素渲染成canvas,并插入到body中
jsPDF
jsPDF庫可以用于瀏覽器端生成PDF。
文字生成PDF
使用方法如下:
// 默認a4大小,豎直方向,mm單位的PDF var doc = new jsPDF(); // 添加文本‘Download PDF’ doc.text('Download PDF!', 10, 10); doc.save('a4.pdf');
在線演示demo2
圖片生成PDF
使用方法如下:
// 三個參數(shù),第一個方向,第二個單位,第三個尺寸格式 var doc = new jsPDF('landscape','pt',[205, 115]) // 將圖片轉(zhuǎn)化為dataUrl var imageData = ‘data:image/png;base64,iVBORw0KGgo...’; doc.addImage(imageData, 'PNG', 0, 0, 205, 115); doc.save('a4.pdf');
在線演示demo3
文字與圖片生成PDF
// 三個參數(shù),第一個方向,第二個尺寸,第三個尺寸格式 var doc = new jsPDF('landscape','pt',[205, 155]) // 將圖片轉(zhuǎn)化為dataUrl var imageData = ‘data:image/png;base64,iVBORw0KGgo...’; //設(shè)置字體大小 doc.setFontSize(20); //10,20這兩參數(shù)控制文字距離左邊,與上邊的距離 doc.text('Stone', 10, 20); // 0, 40, 控制文字距離左邊,與上邊的距離 doc.addImage(imageData, 'PNG', 0, 40, 205, 115); doc.save('a4.pdf')
在線演示demo4
生成pdf需要把轉(zhuǎn)化的元素添加到j(luò)sPDF實例中,也有添加html的功能,但某些元素?zé)o法生成在pdf中,因此可以使用html2canvas + jsPDF的方式將頁面轉(zhuǎn)成pdf。通過html2canvas將遍歷頁面元素,并渲染生成canvas,然后將canvas圖片格式添加到j(luò)sPDF實例,生成pdf。
html2canvas + jsPDF
單頁
將demo1的例子修改下:
<script type="text/javascript" src="./js/jsPdf.debug.js"></script> <script type="text/javascript"> var downPdf = document.getElementById("renderPdf"); downPdf.onclick = function() { html2canvas(document.body, { onrendered:function(canvas) { //返回圖片dataURL,參數(shù):圖片格式和清晰度(0-1) var pageData = canvas.toDataURL('image/jpeg', 1.0); //方向默認豎直,尺寸ponits,格式a4[595.28,841.89] var pdf = new jsPDF('', 'pt', 'a4'); //addImage后兩個參數(shù)控制添加圖片的尺寸,此處將頁面高度按照a4紙寬高比列進行壓縮 pdf.addImage(pageData, 'JPEG', 0, 0, 595.28, 592.28/canvas.width * canvas.height ); pdf.save('stone.pdf'); } }) } </script>
在線演示demo5
如果頁面內(nèi)容根據(jù)a4比例轉(zhuǎn)化后高度超過a4紙高度呢,生成的pdf會怎么樣?會分頁嗎?
你可以試試,驗證一下自己的想法: demo6
jsPDF提供了一個很有用的API,addPage(),我們可以通過pdf.addPage(),來添加一頁pdf,然后通過pdf.addImage(...),將圖片賦予這頁pdf來顯示。
那么我們?nèi)绾未_定哪里分頁?
這個問題好回答,我們可以設(shè)置一個pageHeight,超過這個高度的內(nèi)容放入下一頁pdf。
來捋一下思路,將html頁面內(nèi)容生成canvas圖片,通過addImage將第一頁圖片添加到pdf中,超過一頁內(nèi)容,通過addPage()添加pdf頁數(shù),然后再通過addImage將下一頁圖片添加到pdf中。
嗯~,很好!巴特,難道沒有發(fā)現(xiàn)問題嗎?
這個方法實現(xiàn)的前提是 — — 我們能根據(jù)pageHeight先將整頁內(nèi)容生成的canvas圖片分割成對應(yīng)的小圖片,然后一個蘿卜一個坑,一頁一頁addImage進去。
What? 想一想我們的canvas是腫么來的,不用拉上去,直接看下面:
html2canvas(document.body, { onrendered:function(canvas) { //it is here we handle the canvas } })
這里的body就是要生成canvas的元素對象,一個元素生成一個canvas;那么我們需要一頁一頁的canvas,也就是說。。。
你覺得可能嗎? 我覺得不太現(xiàn)實,按這思路要獲取頁面上不同位置的DOM元素,然后通過htnl2canvas(element,option)來處理,先不說能不能剛好在每個pageHeight的位置剛好找到一個DOM元素,就算找到了,這樣做累不累。
累的話
:)可以看看下面這種方法
多頁
我提供的思路是我們只生成一個canvas,對就一個,轉(zhuǎn)化元素就是你要轉(zhuǎn)成pdf內(nèi)容的母元素,在這篇demo里就是body了;其他不變,也是超過一頁內(nèi)容就addPage,然后addImage,只不過這里添加的是同一個canvas。
當(dāng)然這樣做只會出現(xiàn)多頁重復(fù)的pdf,那到底怎么實現(xiàn)正確分頁顯示。其實主要利用了jsPDF的兩點:
- 超過jsPDF實例格式尺寸的內(nèi)容不顯示 (var pdf = new jsPDF('', 'pt', 'a4'); demo中就是a4紙的尺寸) - addImage有兩個參數(shù)可以控制圖片在pdf中的位置
雖然每一頁pdf上顯示的圖片是相同的,但我們通過調(diào)整圖片的位置,產(chǎn)生了分頁的錯覺。以第二頁為例,將豎直方向上的偏移設(shè)置為-841.89即一張a4紙的高度,又因為超過a4紙高度范圍的圖片不顯示,所以第二頁顯示了圖片豎直方向上[841.89,1682.78]范圍內(nèi)的內(nèi)容,這就得到了分頁的效果,以此類推。
還是看代碼吧:
html2canvas(document.body, { onrendered:function(canvas) { var contentWidth = canvas.width; var contentHeight = canvas.height; //一頁pdf顯示html頁面生成的canvas高度; var pageHeight = contentWidth / 592.28 * 841.89; //未生成pdf的html頁面高度 var leftHeight = contentHeight; //頁面偏移 var position = 0; //a4紙的尺寸[595.28,841.89],html頁面生成的canvas在pdf中圖片的寬高 var imgWidth = 595.28; var imgHeight = 592.28/contentWidth * contentHeight; var pageData = canvas.toDataURL('image/jpeg', 1.0); var pdf = new jsPDF('', 'pt', 'a4'); //有兩個高度需要區(qū)分,一個是html頁面的實際高度,和生成pdf的頁面高度(841.89) //當(dāng)內(nèi)容未超過pdf一頁顯示的范圍,無需分頁 if (leftHeight < pageHeight) { pdf.addImage(pageData, 'JPEG', 0, 0, imgWidth, imgHeight ); } else { while(leftHeight > 0) { pdf.addImage(pageData, 'JPEG', 0, position, imgWidth, imgHeight) leftHeight -= pageHeight; position -= 841.89; //避免添加空白頁 if(leftHeight > 0) { pdf.addPage(); } } } pdf.save('content.pdf'); } })
在線演示demo7
兩邊留邊距
修改imgWidth,并且在addImage時x方向參數(shù)設(shè)置你要的邊距,具體代碼如下
var imgWidth = 555.28; var imgHeight = 555.28/contentWidth * contentHeight; ... pdf.addImage(pageData, 'JPEG', 20, 0, imgWidth, imgHeight ); ... pdf.addImage(pageData, 'JPEG', 20, position, imgWidth, imgHeight);
在線演示demo8
:如何用 JS 一次獲取 HTML 表單的所有字段 ?
考慮一個簡單的 HTML 表單,用于將任務(wù)保存在待辦事項列表中:
<form>
<label for="name">用戶名</label>
<input type="text" id="name" name="name" required>
<label for="description">簡介</label>
<input type="text" id="description" name="description" required>
<label for="task">任務(wù)</label>
<textarea id="task" name="task" required></textarea>
<button type="submit">提交</button>
</form>
上面每個字段都有對應(yīng)的的type,ID和 name屬性,以及相關(guān)聯(lián)的label。用戶單擊“提交”按鈕后,我們?nèi)绾螐拇吮韱沃蝎@取所有數(shù)據(jù)?
有兩種方法:一種是用黑科技,另一種是更清潔,也是最常用的方法。為了演示這種方法,我們先創(chuàng)建form.js,并引入文件中。
首先,我們在表單上為Submit事件注冊一個事件偵聽器,以停止默認行為(它們將數(shù)據(jù)發(fā)送到后端)。
然后,使用this.elements或event.target.elements訪問表單字段:
相反,如果需要響應(yīng)某些用戶交互而動態(tài)添加更多字段,那么我們需要使用FormData。
首先,我們在表單上為submit事件注冊一個事件偵聽器,以停止默認行為。接著,我們從表單構(gòu)建一個FormData對象:
const form = document.forms[0];
form.addEventListener("submit", function(event) {
event.preventDefault();
const formData = new FormData(this);
});
除了append()、delete()、get()、set()之外,F(xiàn)ormData 還實現(xiàn)了Symbol.iterator。這意味著它可以用for...of 遍歷:
const form = document.forms[0];
form.addEventListener("submit", function(event) {
event.preventDefault();
const formData = new FormData(this);
for (const formElement of formData) {
console.log(formElement);
}
})
除了上述方法之外,entries()方法獲取表單對象形式:
const form = document.forms[0];
form.addEventListener("submit", function(event) {
event.preventDefault();
const formData = new FormData(this);
const entries = formData.entries();
const data = Object.fromEntries(entries);
});
這也適合Object.fromEntries() (ECMAScript 2019)
為什么這有用?如下所示:
const form = document.forms[0];
form.addEventListener("submit", function(event) {
event.preventDefault();
const formData = new FormData(this);
const entries = formData.entries();
const data = Object.fromEntries(entries);
// send out to a REST API
fetch("https://some.endpoint.dev", {
method: "POST",
body: JSON.stringify(data),
headers: {
"Content-Type": "application/json"
}
})
.then(/**/)
.catch(/**/);
});
一旦有了對象,就可以使用fetch發(fā)送有效負載。
小心:如果在表單字段上省略name屬性,那么在FormData對象中剛沒有生成。
要從HTML表單中獲取所有字段,可以使用:
使用FormData構(gòu)建具有所有字段的對象,之后可以轉(zhuǎn)換,更新或?qū)⑵浒l(fā)送到遠程API。*
作者:VALENTINO GAGLIARDI 譯者:前端小智 來源:valentinog
原文:https://www.valentinog.com/blog/form-data/
*請認真填寫需求信息,我們會在24小時內(nèi)與您取得聯(lián)系。