先看一下目前的效果:在「Markdown 筆記」原有的上傳圖片彈窗中增長了一個咱們自定義的上傳按鈕,經過直接與後端 API 交互完成圖片上傳(相關 API 是「筆記」上傳時公開使用的)。javascript
兩年前還沒開始使用 GitHub 記錄讀書筆記,那時在用有道雲筆記。我使用的是 「Markdown 筆記」,在最開始一段時間沒有上傳圖片的需求,因此用起來還能夠。後來開始記錄《Head First 設計模式》的讀書筆記,並畫了每一個模式的類圖,開始有了上傳圖片的需求,而官方將 「Markdown 筆記」上傳圖片的功能僅對會員開放。java
窮則思變,機智的我就注意到了之前使用「筆記」時能夠直接上傳圖片,而且沒有會員限制,將這個圖片的連接放到「Markdown 筆記」內也可正常使用。因此就先人工操做,每次須要上傳圖片時,先切到一個本身創建的用於上傳圖片的「筆記」,圖片上傳成功後再將連接拷貝回「Markdown 筆記」,暫時解決了上傳圖片的需求。node
懶是第一輩子產力,作程序員最大的好處就是能夠經過寫代碼簡化平常網上活動的各類重複性操做。在按照前面的方式完成幾篇帶圖片的「Markdown 筆記」後,就開始感到厭煩,這一操做機械重複沒有任何價值,因此就想到很適合經過代碼自動執行。git
因爲咱們須要的圖片上傳功能在「Markdown 筆記」頁面中沒有,因此不能使用操做頁面元素的方式,只能經過抓 API ,而且本身調用 API 來實現圖片上傳。程序員
封裝 API 前咱們須要抓 API ,這個很簡單,其實就是觸發一下咱們所須要實現的功能,而後查看瀏覽器發送了哪些請求,記住這些請求並封裝一下,以便後續調用。github
「筆記」圖片上傳操做後會發現瀏覽器發送了三個請求:編程
transmitId
以供後續兩個請求使用transmitId
上傳圖片(這裏僅實現了小文件單次上傳)transmitId
給上傳完成的文件添加各類信息,並獲取圖片地址一個簡單的圖片上傳只須要三個請求,因此咱們先封裝一下,具體實現能夠在 api.js 找到(其中還封裝了其餘 API ,不事後續沒有使用到)後端
點擊完上傳後,咱們須要一個組件來實現選擇圖片、上傳圖片、返回圖片地址這三個操做。咱們在前面封裝的 API 已經實現了上傳圖片並返回圖片地址的功能,因此在這裏咱們這個組件只須要能觸發選擇圖片邏輯便可。咱們能夠經過 <input type="file">
來實現選擇文件的功能,而後咱們須要對其註冊 change
事件,用於當用戶選擇完圖片後,實現後續的操做邏輯。設計模式
// 上傳文件,觸發後,會選擇文件,並執行上傳文件獲取url,最後執行回調(回調的第一個參數是文件,第二個參數是上傳的url) function upload(accept, callback) { // 1. 建立 input 節點 if($('#diy-uploader-input').length == 0) { $('body').append('<input id="diy-uploader-input" type="file" style="position: absolute; top: -1000px; left: -1000px;" accept="' + accept + '">'); } // 2. 並綁定點擊事件,用於觸發 實際執行上傳 var $this = this; $('#diy-uploader-input').on('change', function(event) { var file = event.target.files[0]; var url = $this.doUpload(file); // 執行回調 callback(file, url); }); // 3. 執行模擬點擊 $('#diy-uploader-input').click(); }
如今咱們已經擁有了上傳圖片的能力,接下來就是要將這個能力添加到咱們的「Markdown 筆記」中,咱們須要支持兩個功能:api
當時還沒怎麼接觸過 HTML
和 JavaScript
,但這兩個功能比較簡單,編程的基本原理也沒有使用新的知識體系,因此很快就能寫出能完成功能的代碼(省略中間處理各類問題的過程)。
// 初始化,當md文件上傳彈框出來的時候,添加上傳圖片按鈕 function init() { // 有道雲筆記用 on 綁定 DOMNodeInserted 不生效 - -||| $('body')[0].addEventListener("DOMNodeInserted", function(e){ // 若是是 markdown 上傳圖片的節點被添加 if(e.target.nodeName.toLowerCase()== 'markdown-upload-image') { var divButtonBarSelector = 'body > dialog-overlay > div > div > div.widget-dialog-body > markdown-upload-image > div > div.button-bar'; // 若是 底部按鈕欄已出來,而且沒添加過 上傳按鈕,則添加 上傳按鈕 if($(divButtonBarSelector).length == 1 && $('#diy-uploader-button').length == 0) { // 添加按鈕 var uploaderButton = '<div id="diy-uploader-button" class="loadbtn local-img" style="margin-right:15px;height:34px">上傳圖片</div>'; $('body > dialog-overlay > div > div > div.widget-dialog-body > markdown-upload-image > div > div.button-bar').prepend(uploaderButton); // 給按鈕添加事件 $('#diy-uploader-button').on('click', function() { component.uploader.upload('image/*', feature.mdImageUploader.backfillPage); }); } } }, false); } // 回填頁面 function backfillPage(file, url) { // 填入url var urlSelector = 'body > dialog-overlay > div > div > div.widget-dialog-body > markdown-upload-image > div > div:nth-child(2) > div.edit-container > input'; $(urlSelector).val(url); // 觸發 input 事件,更新雙向綁定的數據 tool.trigger(urlSelector, 'input'); // 填入文件名 var nameSelector = 'body > dialog-overlay > div > div > div.widget-dialog-body > markdown-upload-image > div > div:nth-child(3) > div.edit-container > input'; var name = file.name.substring(0, file.name.lastIndexOf('.')); $(nameSelector).val(name); // 觸發 input 事件,更新雙向綁定的數據 tool.trigger(nameSelector, 'input'); }
至此咱們已經實現了有道雲筆記支持「Markdown 筆記」上傳圖片的功能。能夠直接將 loader.js 中的代碼拷貝至 Tampermonkey 中便可實現非會員上傳。
這一段腳本是兩年前寫的,可是至今仍舊能夠使用。雖然時隔好久,腳本實現的具體細節早已忘記,可是當我看到我這豐富的註釋時,仍是能夠回想起來當時想法及每段代碼的邏輯。寫註釋也是我一直堅持的好習慣,平時寫業務代碼中沒有這麼詳細的註釋去解釋每一行的操做邏輯,但仍舊會在每一段相對獨立的操做開始時註明其功能等信息。
本文首發於公衆號:滿賦諸機( 點擊查看原文) 開源在 GitHub : reading-notes/tampermonkey/note-youdao