Baseline Widely available
带æ type="file"
ç <input>
å
ç´ å
è®¸ç¨æ·å¯ä»¥ä»ä»ä»¬ç设å¤ä¸éæ©ä¸ä¸ªæå¤ä¸ªæä»¶ãéæ©åï¼è¿äºæä»¶å¯ä»¥ä½¿ç¨æäº¤è¡¨åçæ¹å¼ä¸ä¼ å°æå¡å¨ä¸ï¼æè
éè¿ Javascript 代ç åæä»¶ API 对æä»¶è¿è¡æä½ã
<label for="avatar">Choose a profile picture:</label>
<input type="file" id="avatar" name="avatar" accept="image/png, image/jpeg" />
label {
display: block;
font:
1rem "Fira Sans",
sans-serif;
}
input,
label {
margin: 0.4rem 0;
}
å¼
æä»¶ input ç value
屿§å
å«äºä¸ä¸ªå符串ï¼è¡¨ç¤ºå·²éæ©æä»¶çè·¯å¾ãå¦æç¨æ·æ²¡æéæ©ä»»ä½æä»¶ï¼å该å¼ä¸ºç©ºå符串ï¼""
ï¼ãå¦æç¨æ·éæ©äºå¤ä¸ªæä»¶ï¼å value
表示ä»ä»¬éæ©çæä»¶å表ä¸ç第ä¸ä¸ªæä»¶ãå¯ä»¥ä½¿ç¨ input ç HTMLInputElement.files
屿§æ è¯å
¶ä»æä»¶ã
夿³¨ï¼ 为äºé»æ¢æ¶æè½¯ä»¶çæµæä»¶è·¯å¾ï¼è¯¥å¼çåç¬¦ä¸²è¡¨ç¤ºæ»æ¯ä»¥ C:\fakepath\
为åç¼çæä»¶åï¼è并䏿¯æä»¶ççå®è·¯å¾ã
é¤äºè¢«ææ <input>
å
ç´ å
±äº«çå
Œ
±å±æ§ï¼file
ç±»åç input è¿æ¯æä¸å屿§ï¼
accept
屿§æ¯ä¸ä¸ªå符串ï¼å®å®ä¹äºæä»¶ input åºè¯¥æ¥åçæä»¶ç±»åãè¿ä¸ªå符串æ¯ä¸ä¸ªä»¥éå·ä¸ºåéçå¯ä¸æä»¶ç±»å说æç¬¦å表ãç±äºç»å®çæä»¶ç±»åå¯ä»¥ç¨å¤ç§æ¹å¼æå®ï¼å æ¤å½ä½ éè¦ç»å®æ ¼å¼çæä»¶æ¶ï¼æä¾ä¸ç»å®æ´çç±»åæå®ç¬¦æ¯é常æç¨çã
ä¾å¦ï¼å¯ä»¥éè¿å¤ç§æ¹å¼è¯å« Microsoft Word æä»¶ï¼å æ¤æ¥å Word æä»¶çç½ç«å¯è½ä½¿ç¨åè¿æ ·ç <input>
ï¼
<input
type="file"
id="docpicker"
accept=".doc,.docx,application/msword,application/vnd.openxmlformats-officedocument.wordprocessingml.document" />
capture
capture
屿§æ¯ä¸ä¸ªå符串ï¼å¦æ accept
屿§æåºäº input æ¯å¾çæè
è§é¢ç±»åï¼å宿å®äºä½¿ç¨åªä¸ªæå头å»è·åè¿äºæ°æ®ãå¼ user
表示åºè¯¥ä½¿ç¨åç½®æå头åï¼æï¼éº¦å
é£ãå¼ environment
表示åºè¯¥ä½¿ç¨åç½®æå头åï¼æï¼éº¦å
é£ãå¦æç¼ºå°æ¤å±æ§ï¼åç¨æ·ä»£çå¯ä»¥èªç±å³å®åä»ä¹ãå¦æè¯·æ±çå置模å¼ä¸å¯ç¨ï¼åç¨æ·ä»£çå¯è½éåå°å
¶é¦éçé»è®¤æ¨¡å¼ã
夿³¨ï¼ capture
以忝ä¸ä¸ªå¸å°ç±»åç屿§ï¼å¦æåå¨ï¼å请æ±ä½¿ç¨è®¾å¤çåªä½æè·è®¾å¤ï¼å¦ï¼æåæºï¼ï¼è䏿¯è¯·æ±ä¸ä¸ªæä»¶è¾å
¥ã
彿å®å¸å°ç±»å屿§ multiple
æ¶ï¼æä»¶ input å
è®¸ç¨æ·éæ©å¤ä¸ªæä»¶ã
é¤äºä¸é¢ååºæ¥ç屿§ï¼ä¸é¢çéæ å屿§å¨æäºæµè§å¨ä¸å¯ç¨ãåºè¯¥å°½éé¿å 使ç¨å®ä»¬ï¼å ä¸ºè¿æ ·åå°éå¶ä»£ç 卿²¡æå®ç°å®ä»¬çæµè§å¨ä¸çè¿è¡è½åã
webkitdirectory
妿åºç°å¸å°å±æ§ webkitdirectory
ï¼è¡¨ç¤ºå¨æä»¶éæ©å¨çé¢ä¸ç¨æ·åªè½éæ©ç®å½ãæ´å¤ç»èå示ä¾è§ HTMLInputElement.webkitdirectory
ã
尽管 webkitdirectory
æåä»
为åºäº Webkit çæµè§å¨å®ç°ï¼å®è¿å¨ Microsoft Edge å Firefox 50 åå
¶åçæ¬ä¸å¯ç¨ãç¶èï¼å°½ç®¡å®æç¸å¯¹å¹¿æ³çæ¯æï¼å®ä»ç¶æ¯éæ åçãé¤é嫿 éæ©ï¼å¦åä¸è¦ä½¿ç¨å®ã
å¯ä¸æä»¶ç±»å说æç¬¦æ¯ä¸ä¸ªå符串ï¼è¡¨ç¤ºå¨ file
ç±»åç <input>
å
ç´ ä¸ç¨æ·å¯ä»¥éæ©çæä»¶ç±»åãæ¯ä¸ªå¯ä¸æä»¶ç±»å说æç¬¦å¯ä»¥éç¨ä¸åå½¢å¼ä¹ä¸ï¼
.jpg
ã.pdf
æ .doc
ãaudio/*
ï¼è¡¨ç¤ºâä»»ä½é³é¢æä»¶âãvideo/*
ï¼è¡¨ç¤ºâä»»ä½è§é¢æä»¶âãimage/*
ï¼è¡¨ç¤ºâä»»ä½å¾çæä»¶âãaccept
屿§ç弿¯å
å«ä¸ä¸ªæå¤ä¸ªï¼ç¨éå·åéï¼å¯ä¸æä»¶ç±»å说æç¬¦çå符串ãä¾å¦ï¼ä¸ä¸ªæä»¶éæ©å¨éè¦è½è¢«è¡¨ç¤ºæä¸å¼ å¾ççå
容ï¼å
æ¬æ åçå¾çæ ¼å¼å PDF æä»¶ï¼å¤§æ¦æ¯è¿æ ·çï¼
<input type="file" accept="image/*,.pdf" />
ä½¿ç¨æä»¶è¾å
¥ åºæ¬ç¤ºä¾
<form method="post" enctype="multipart/form-data">
<div>
<label for="file">éæ©è¦ä¸ä¼ çæä»¶</label>
<input type="file" id="file" name="file" multiple />
</div>
<div>
<button>æäº¤</button>
</div>
</form>
div {
margin-bottom: 10px;
}
产çå¦ä¸ç»æï¼
夿³¨ï¼ ä½ ä¹å¯ä»¥å¨ GitHub 䏿¾å°è¿ä¸ªç¤ºä¾ââè¯¦è§æºä»£ç åå¨çº¿è¿è¡å®ä¾ã
æ è®ºç¨æ·çè®¾å¤ææä½ç³»ç»æ¯ä»ä¹ï¼æä»¶è¾å ¥é½æä¾ä¸ä¸ªæé®ï¼æå¼ä¸ä¸ªå è®¸ç¨æ·éæ©æä»¶çæä»¶éæ©å¯¹è¯æ¡ã
éè¿å
å«ä¸è¿°ç multiple
屿§ï¼å¯ä»¥æå®ä¸æ¬¡éæ©å¤ä¸ªæä»¶ãç¨æ·å¯ä»¥ç¨ä»ä»¬éæ©çå¹³å°å
许ç任使¹å¼ä»æä»¶éæ©å¨ä¸éæ©å¤ä¸ªæä»¶ï¼å¦æä½ Shift æ Controlï¼ç¶ååå»ï¼ãå¦æåªæ³è®©ç¨æ·ä¸ºæ¯ä¸ª <input>
éæ©ä¸ä¸ªæä»¶ï¼é£ä¹çç¥ multiple
屿§ã
è¢«éæ©çæä»¶ä»¥ HTMLInputElement.files
屿§è¿åï¼å®æ¯å
å«ä¸ç³»å File
对象ç FileList
对象ãFileList
çè¡ä¸ºåä¸ä¸ªæ°ç»ï¼å¯ä»¥éè¿æ£æ¥ length
屿§æ¥è·å¾å·²éæ©æä»¶çæ°éã
æ¯ä¸ª File
å
å«ä¸åä¿¡æ¯ï¼
name
æä»¶åã
lastModified
ä¸ä¸ªæ°åï¼æå®æä»¶æå䏿¬¡ä¿®æ¹çæ¥æåæ¶é´ï¼ä»¥ UNIX æ°çºªå ï¼1970 å¹´ 1 æ 1 æ¥åå¤ï¼ä»¥æ¥çæ¯«ç§æ°è¡¨ç¤ºã
lastModifiedDate
å·²å¼ç¨
ä¸ä¸ª Date
对象ï¼è¡¨ç¤ºæä»¶æå䏿¬¡ä¿®æ¹çæ¥æåæ¶é´ãè¿è¢«å¼ç¨ï¼å¹¶ä¸ä¸åºä½¿ç¨ãä½¿ç¨ lastModified
ä½ä¸ºæ¿ä»£ã
size
以åèæ°ä¸ºåä½çæä»¶å¤§å°ã
type
æä»¶ç MIME ç±»åã
webkitRelativePath
éæ å
ä¸ä¸ªåç¬¦ä¸²ï¼æå®äºç¸å¯¹äºå¨ç®å½éæ©å¨ä¸éæ©çåºæ¬ç®å½çæä»¶è·¯å¾ï¼å³ï¼ä¸ä¸ªè®¾ç½®äº webkitdirectory
屿§ç file
éæ©å¨ï¼ãè¿æ¯éæ åçï¼åºè¯¥è°¨æ
使ç¨ã
夿³¨ï¼ ä½ å¯ä»¥å¨ææç°ä»£æµè§å¨ä¸è¯»å HTMLInputElement.files
çå¼ï¼è¯¥ç¹æ§å·²ç»æ·»å å°äº Firefox 57 ä¸ï¼è§ Firefox bug 1384030ï¼ã
é常ï¼ä½ ä¸å¸æç¨æ·è½å¤éæ©ä»»æç±»åçæä»¶ï¼ç¸åï¼ä½ é叏叿å®ä»¬éæ©ç¹å®ç±»åçæä»¶ãä¾å¦ï¼å¦æä½ çæä»¶è¾å ¥è®©ç¨æ·ä¸ä¼ ä¸ªäººèµæå¾çï¼ä½ å¯è½å¸æä»ä»¬éæ© Web å ¼å®¹çå¾åæ ¼å¼ï¼å¦ JPEG æ PNGã
å¯ä»¥ç¨ accept
屿§æå®å¯æ¥åçæä»¶ç±»åï¼å®æ¯ä¸ä¸ªä»¥éå·é´éçæä»¶æ©å±åå MIME ç±»åå表ãä¸äºä¾åå¦ä¸æç¤ºï¼
accept="image/png"
æ accept=".png"
ââæ¥å PNG æä»¶ãaccept="image/png, image/jpeg"
æ accept=".png, .jpg, .jpeg"
ââæ¥å PNG æ JPEG æä»¶ãaccept="image/*"
ââæ¥åä»»ä½å¸¦æ image/*
MIME ç±»åçæä»¶ãï¼è®¸å¤ç§»å¨è®¾å¤ä¹å
è®¸ç¨æ·å¨ä½¿ç¨å®æ¶ç¨æå头æç
§ãï¼accept=".doc,.docx,.xml,application/msword,application/vnd.openxmlformats-officedocument.wordprocessingml.document"
ââæ¥åç±»ä¼¼äº MS Word ææ¡£ç任使件ã让æä»¬æ¥çä¸ä¸ªæ´å®æ´çä¾åï¼
<form method="post" enctype="multipart/form-data">
<div>
<label for="profile_pic">éæ©è¦ä¸ä¼ çæä»¶</label>
<input
type="file"
id="profile_pic"
name="profile_pic"
accept=".jpg, .jpeg, .png" />
</div>
<div>
<button>æäº¤</button>
</div>
</form>
div {
margin-bottom: 10px;
}
ä¸è¿°ä»£ç 产çåä¹å示ä¾ç¸ä¼¼çç»æï¼
夿³¨ï¼ ä½ ä¹å¯ä»¥å¨ GitHub 䏿¾å°è¿ä¸ªç¤ºä¾ââè¯¦è§æºä»£ç åå¨çº¿è¿è¡å®ä¾ã
è¿å¯è½çèµ·æ¥å¾ç¸ä¼¼ï¼ä½æ¯å¦æä½ å°è¯ç¨è¯¥è¾å
¥éæ©ä¸ä¸ªæä»¶ï¼ä½ å°çå°æä»¶éæ©å¨åªå
è®¸ä½ éæ© accept
弿å®çæä»¶ç±»åï¼å®é
æ¥å£ä¼æä¸åçæµè§å¨åæä½ç³»ç»ææä¸åï¼ã
accept
屿§ä¸éªè¯æéæä»¶çç±»åï¼å®åªæ¯ä¸ºæµè§å¨æä¾æç¤ºæ¥å¼å¯¼ç¨æ·éæ©æ£ç¡®çæä»¶ç±»åãç¨æ·ä»ç¶å¯ä»¥ï¼å¨å¤§å¤æ°æ
åµä¸ï¼å¨æä»¶éæ©å¨ä¸åæ¢ä¸ä¸ªé项ï¼ä½¿å
¶è½å¤è¦çæ¤é项并鿩ä»ä»¬å¸æç任使件ï¼ç¶å鿩䏿£ç¡®çæä»¶ç±»åã
å æ¤ï¼åºè¯¥ç¡®ä¿ accept
屿§å¾å°éå½çæå¡å¨ç«¯éªè¯çæ¯æã
ä¸è½ä»èæ¬ä¸è®¾ç½®æä»¶éåå¨çå¼ââåä¸é¢è¿æ ·çäºæ æ¯æ²¡æææçï¼
const input = document.querySelector("input[type=file]");
input.value = "foo";
å½ä½¿ç¨ <input type="file">
éæ©æä»¶æ¶ï¼åºäºææ¾çå®å
¨åå ï¼æºæä»¶çå®é
è·¯å¾æ²¡ææ¾ç¤ºå¨ input ç value
屿§ä¸ãå®é
䏿¾ç¤ºäºæä»¶åï¼å¹¶ç¨ C:\fakepath\
éå å¨è·¯å¾çå¼å¤´ãè¿ç§æªçæä¸äºåå²åå ï¼ä½å®å¨ææç°ä»£æµè§å¨ä¸é½åå°æ¯æï¼èä¸å®é
ä¸å¨è§èä¸ä¹æå®ä¹ã
卿¬ä¾ä¸ï¼æä»¬å°å±ç¤ºä¸ä¸ªç¨å¾®é«çº§ä¸ç¹çæä»¶éæ©å¨ï¼å®å©ç¨äºå¨ HTMLInputElement.files
屿§ä¸å¯ç¨çæä»¶ä¿¡æ¯ï¼å¹¶ä¸å±ç¤ºäºä¸äºèªæçæå·§ã
夿³¨ï¼ ä½ å¯ä»¥å¨ GitHub 䏿¥çæ¬ç¤ºä¾ç宿´æºä»£ç ââfile-example.htmlï¼åè§å¨çº¿çè¿è¡å®ä¾ï¼ãæä»¬ä¸ä¼è§£é CSSï¼JavaScript æ¯ä¸»è¦çå ³æ³¨ç¹ã
é¦å ï¼è®©æä»¬çç HTMLï¼
<form method="post" enctype="multipart/form-data">
<div>
<label for="image_uploads">Choose images to upload (PNG, JPG)</label>
<input
type="file"
id="image_uploads"
name="image_uploads"
accept=".jpg, .jpeg, .png"
multiple />
</div>
<div class="preview">
<p>No files currently selected for upload</p>
</div>
<div>
<button>Submit</button>
</div>
</form>
html {
font-family: sans-serif;
}
form {
width: 580px;
background: #ccc;
margin: 0 auto;
padding: 20px;
border: 1px solid black;
}
form ol {
padding-left: 0;
}
form li,
div > p {
background: #eee;
display: flex;
justify-content: space-between;
margin-bottom: 10px;
list-style-type: none;
border: 1px solid black;
}
form img {
height: 64px;
order: 1;
}
form p {
line-height: 32px;
padding-left: 10px;
}
form label,
form button {
background-color: #7f9ccb;
padding: 5px 10px;
border-radius: 5px;
border: 1px ridge black;
font-size: 0.8rem;
height: auto;
}
form label:hover,
form button:hover {
background-color: #2d5ba3;
color: white;
}
form label:active,
form button:active {
background-color: #0d3f8f;
color: white;
}
è¿åæä»¬ä¹åçå°çç±»ä¼¼ï¼æ²¡æä»ä¹è¦è¯´æçã
æ¥ä¸æ¥ï¼è®©æä»¬çä¸ä¸ JavaScriptã
å¨èæ¬ç第ä¸è¡ï¼æä»¬è·å¾äºå¯¹è¡¨å input æ¬èº«åæ¥æ .preview
ç±»ç <div>
å
ç´ çå¼ç¨ãç¶åï¼æä»¬éèäº <input>
å
ç´ ï¼è¿æ ·åæ¯å 为æä»¶è¾å
¥å¾å¾å¾é¾çï¼é¾äºè®¾è®¡æ ·å¼ï¼èä¸å¨è·¨æµè§å¨ä¸å¯¹å®ä»¬ç设计ä¸ä¸è´ãä½ å¯ä»¥éè¿åå» <label>
é¨åæ¥æ¿æ´» input
å
ç´ ï¼å æ¤ï¼æå¥½å¨è§è§ä¸éè input
å¹¶å° label 设计ææé®çæ ·å¼ï¼è¿æ ·ç¨æ·å¦ææ³ä¸ä¼ æä»¶å°±ä¼ç¥éå¦ä½ä¸ä¹äº¤äºã
const input = document.querySelector("input");
const preview = document.querySelector(".preview");
input.style.opacity = 0;
夿³¨ï¼ ä½¿ç¨ opacity
æ¥éèæä»¶è¾å
¥ï¼è䏿¯ä½¿ç¨ visibility: hidden
æè
display: none
ï¼å 为è¾
婿æ¯å°åä¸¤ç§æ ·å¼è§£é为æä»¶ input æ¯ä¸å¯äº¤äºçã
æ¥ä¸æ¥ï¼æä»¬å°äºä»¶çå¬å¨æ·»å å° input ä¸ï¼ä»¥çå¬éæ©çå¼çæ´æ¹ï¼å¨æ¬ä¾ä¸ï¼å½éæ©æä»¶æ¶ï¼ãäºä»¶çå¬å¨è°ç¨æä»¬å®å¶ç updateImageDisplay()
彿°ã
input.addEventListener("change", updateImageDisplay);
æ¯å½ updateImageDisplay()
彿°è¢«è°ç¨æ¶ï¼æä»¬ï¼
使ç¨ä¸ä¸ª while
å¾ªç¯æ¥æ¸
空é¢è§åº <div>
çä¸çå
容ã
è·åå
嫿æå·²éæ©æä»¶ä¿¡æ¯ç FileList
对象ï¼å¹¶å°å
¶ç¨ä¸ä¸ªåé curFiles
ä¿åã
éè¿æ£æ¥ curFiles.length
æ¯å¦çäº 0 æ¥æ£æ¥æ¯å¦æ²¡ææä»¶è¢«éæ©ã妿æ¯ï¼ååé¢è§åº <div>
è¾åºä¸æ¡æ¶æ¯ï¼è¡¨ç¤ºæ²¡æéæ©æä»¶ã
å¦æéæ©äºæä»¶ï¼æä»¬å°å¾ªç¯éåæ¯ä¸ªæä»¶ï¼å¹¶å°å
³äºå®çä¿¡æ¯è¾åºå°é¢è§åº <div>
ãæ³¨æäºé¡¹ï¼
æä»¬ä½¿ç¨å®å¶ç validFileType()
彿°æ¥æ£æ¥æä»¶çç±»åæ¯å¦æ£ç¡®ï¼ä¾å¦ï¼ç¨ accept
屿§æå®çå¾çç±»åï¼ã
妿æ¯ï¼æä»¬ï¼
<div>
çä¸ä¸ªå表项ä¸ï¼ä» file.name
å file.size
è·åè¿äºä¿¡æ¯ï¼ãå®å¶ç returnFileSize()
彿°è¿åä¸ä¸ªç¨ bytes/KB/MB 表示çå¯è¯»æ§è¯å¥½çæä»¶å¤§å°ï¼é»è®¤æ
åµä¸ï¼æµè§å¨ä»¥ç»å¯¹åèæ°æ¥å大å°ï¼ãURL.createObjectURL(curFiles[i])
æ¥çæå¾ççä¸å¼ 缩ç¥é¢è§å¾ãç¶åï¼éè¿å建ä¸ä¸ªæ°ç <img>
æ¥å°è¿å¼ å¾ç乿å
¥å°å表项ï¼å¹¶ä¸å°å®ç src
设置为缩ç¥å¾ã妿æä»¶ç±»åæ æï¼åå¨åè¡¨é¡¹ä¸æ¾ç¤ºä¸æ¡æ¶æ¯ï¼åè¯ç¨æ·éè¦éæ©ä¸ä¸ªå ¶ä»çæä»¶ç±»åã
function updateImageDisplay() {
while (preview.firstChild) {
preview.removeChild(preview.firstChild);
}
const curFiles = input.files;
if (curFiles.length === 0) {
const para = document.createElement("p");
para.textContent = "No files currently selected for upload";
preview.appendChild(para);
} else {
const list = document.createElement("ol");
preview.appendChild(list);
for (const file of curFiles) {
const listItem = document.createElement("li");
const para = document.createElement("p");
if (validFileType(file)) {
para.textContent = `File name ${file.name}, file size ${returnFileSize(
file.size,
)}.`;
const image = document.createElement("img");
image.src = URL.createObjectURL(file);
listItem.appendChild(image);
listItem.appendChild(para);
} else {
para.textContent = `File name ${file.name}: Not a valid file type. Update your selection.`;
listItem.appendChild(para);
}
list.appendChild(listItem);
}
}
}
å®å¶ç validFileType()
彿°æ¥åä¸ä¸ª File
对象ä½ä¸ºåæ°ï¼ç¶åä½¿ç¨ Array.prototype.includes()
æ£æ¥ fileTypes
䏿¯å¦æå¼åæä»¶ç type
屿§å¹é
ã妿æ¾å°å¹é
项ï¼å½æ°è¿å true
ãå¦ææ²¡æ¾å°ï¼è¿å false
ã
// https://developer.mozilla.org/zh-CN/docs/Web/Media/Formats/Image_types
const fileTypes = [
"image/apng",
"image/bmp",
"image/gif",
"image/jpeg",
"image/pjpeg",
"image/png",
"image/svg+xml",
"image/tiff",
"image/webp",
"image/x-icon",
];
function validFileType(file) {
return fileTypes.includes(file.type);
}
returnFileSize()
彿°æ¥åä¸ä¸ªæ°åï¼åèæ°ï¼åèªå½åæä»¶ç size
屿§ï¼ä½ä¸ºåæ°ï¼å¹¶ä¸å°å
¶è½¬åä¸ºç¨ bytes/KB/MB 表示çå¯è¯»æ§è¯å¥½çæä»¶å¤§å°ã
function returnFileSize(number) {
if (number < 1024) {
return `${number} bytes`;
} else if (number >= 1024 && number < 1048576) {
return `${(number / 1024).toFixed(1)} KB`;
} else if (number >= 1048576) {
return `${(number / 1048576).toFixed(1)} MB`;
}
}
è¿ä¸ªä¾åæ¯è¿æ ·çï¼æ¼ç¤ºä¸ä¸ï¼
è§è æµè§å¨å ¼å®¹æ§ åè§<input type="file">
å File API çæç¨ç¤ºä¾ãRetroSearch is an open source project built by @garambo | Open a GitHub Issue
Search and Browse the WWW like it's 1997 | Search results from DuckDuckGo
HTML:
3.2
| Encoding:
UTF-8
| Version:
0.7.4