主頁 > 前端設計 > 手把手帶你10分鐘手擼一個簡易的Markdown編輯器

手把手帶你10分鐘手擼一個簡易的Markdown編輯器

2021-06-09 22:26:24 前端設計

前言

最近我在專案中需要實作一個 markdown編輯器 的需求,并且是以React框架為開發基礎的,類似掘金這樣的:

img

我的第一想法肯定是能用優秀的開源就一定用開源的,畢竟不能老是重復造輪子,于是我在我的前端群里問了很多群友,他們都給了甩過來一堆開源的markdown編輯器專案,但我一看全是基于Vue使用的,不符合我的預期,逛了一下github,也沒看到我滿意的專案,所以就想自己實作一個啦

需要實作的功能

我們自己實作的話,看看需要支持哪些功能,因為做一個初版的簡易編輯器,所以功能實作得不會太多,但絕對夠用:

  • markdown語法決議,并實時渲染
  • markdown主題css樣式
  • 代碼塊高亮展示
  • 「編輯區」和「展示區」的頁面同步滾動
  • 編輯器工具列中工具的實作

這里先放上我最終實作好了的效果圖:

最終效果圖

我也將本文的代碼放在了 Github 倉庫 (opens new window)上了,歡迎各位點個 ?? star 支持一下

同時,我也給大家提供了一個在線體驗的地址 (opens new window),因為做的比較倉促,歡迎大家給我提意見和pr

具體實作

具體的實作也是按照我們上述列出來的功能的順序來一一實作的

說明:本文通過循序漸進的方式講解,所以重復代碼可能有點多,并且每一部分的注釋是專門用于講解該部分的代碼的,所以在看每一部分功能代碼時,只需要看注釋部分就好~

一、布局

import React, {  } from 'react'


export default function MarkdownEdit() {


    return (
        <div className="markdownEditConainer">
            <textarea className="edit" />
            <div className="show" />
        </div>
    )
}

css樣式我就不一一列舉了,整體就是左邊是編輯區,右邊是展示區,具體樣式如下:

布局圖

二、markdown語法決議

接下來就需要思考如何將 「編輯區」 輸入的markdown語法決議成html標簽并最終渲染在 「展示區」

查找了一下目前比較優秀的markdown決議的開源庫,常用的有三個,分別是MarkedShowdownmarkdown-it ,并借鑒了一下其它大佬的想法,了解了一下這三個庫的優缺點,對比如下:

庫名優點缺點
Marked性能好,正則決議(中文支持比較好)擴展性較差
Showdown擴展性好、正則決議(中文支持好)性能較差
markdown-it擴展性好、性能較好逐字符決議(中文支持不好)

剛開始我選擇了showdown這個庫,因為這個庫使用起來特別方便,而且官方已經在庫中提供了很多擴展功能,只需要配置一些欄位即可,但是后來我又分析了一波,還是選用了markdown-it,因為之后可能需要做更多的語法擴展,showdown的官方檔案寫的比較生硬,而且markdown-it使用的人也多,生態比較好,雖然其官方沒有支持很多擴展的語法,但是已經有很多基于makrdown-it的功能擴展插件了,最重要的是markdown-it的官方檔案寫得好啊(而且有中文檔案)!

接下來寫一下markdown語法決議的代碼吧(其中步驟1、2、3表示的是markdown-it庫的用法)

import React, { useState } from 'react'
// 1. 引入markdown-it庫
import markdownIt from 'markdown-it'

// 2. 生成實體物件
const md = new markdownIt()

export default function MarkdownEdit() {
    const [htmlString, setHtmlString] = useState('')  // 存盤決議后的html字串

    // 3. 決議markdown語法
    const parse = (text: string) => setHtmlString(md.render(text));

    return (
        <div className="markdownEditConainer">
            <textarea 
                className="edit" 
                onChange={(e) => parse(e.target.value)} // 編輯區內容每次修改就更新變數htmlString的值
            />
            <div 
                className="show" 
                dangerouslySetInnerHTML={{ __html: htmlString }} // 將html字串決議成真正的html標簽
            />
        </div>
    )
}

對于將 html字串 轉化為 真正的html標簽 的操作,我們借助了React提供的dangerouslySetInnerHTML屬性,詳細的使用可以看React 官方檔案(opens new window)

此時一個簡單的markdown語法決議功能就實作了,來看看效果

markdown語法決議效果展示圖

兩邊確實正在同步更新,但是…看起來好像哪里不太對!其實是沒問題的,被決議好的 html字串 每個標簽都被附帶上了特定的類名,只是現在我們引入任何的樣式檔案,例如下圖

img

我們可以列印決議出來的html字串看看是什么樣的

<h1 id="">大標題</h1>
<blockquote>
  <p>本文來自公眾號:前端印象</p>
</blockquote>
<pre><code class="js language-js">let name = '零一'
</code></pre>

三、markdown主題樣式

接下來我們可以去網上找一些markdown的主題樣式css檔案,例如我用一個最簡單Github主題的markdown樣式,另外我還是很推薦Typora Theme (opens new window),上面有很多很多的markdown主題

因為我這個樣式主題是有一個前綴id write(Typora上的大部分主題前綴也是#write),所以我們給展示區的標簽加上該類id,并引入樣式檔案

import React, { useState } from 'react'
import './theme/github-theme.css'  // 引入github的markdown主題樣式
import markdownIt from 'markdown-it'

const md = new markdownIt()

export default function MarkdownEdit() {
    const [htmlString, setHtmlString] = useState('')

    const parse = (text: string) => setHtmlString(md.render(text));

    return (
        <div className="markdownEditConainer">
            <textarea 
                className="edit" 
                onChange={(e) => parse(e.target.value)} 
            />
            <div 
                className="show"
                id="write"  // 新增write的ID名 
                dangerouslySetInnerHTML={{ __html: htmlString }}
            />
        </div>
    )
}

再來看看加入樣式后的渲染結果圖

帶樣式的markdown渲染效果圖

四、代碼塊高亮

markdown語法的決議已經完成了,并且也有對應的樣式了,但是代碼塊好像還沒有高亮樣式

這塊兒我們自己來從0到1的實作是不可能的,可以用現成的開源庫 highlight.js,highlight.js 官方檔案 (opens new window),這個庫能幫你做的就是檢測代碼塊標簽元素,并為其加上特定的類名,這里放上這個庫的API檔案(opens new window)

highlight.js 默認是檢測它所支持的所有語言的語法的,我們就不需要關心了,并且其提供了很多的代碼高亮主題,我們可以在官網進行預覽,如下圖所示:

img

更大的好訊息來了!markdown-it已經將highlight.js集成進去了,直接設定一些配置即可,并且我們需要先將該庫下載下來,具體的可以看markdown-it中文官網 - 高亮語法配置(opens new window)

同時在目錄highlight.js/styles/下有很多很多的主題,可以自行匯入

接下來就來實作一下代碼高亮的功能吧

import React, { useState, useEffect } from 'react'
import markdownIt from 'markdown-it'
import './theme/github-theme.css'
import hljs from 'highlight.js'  // 引入highlight.js庫
import 'highlight.js/styles/github.css'  // 引入github風格的代碼高亮樣式

const md = new markdownIt({
    // 設定代碼高亮的配置
    highlight: function (code, language) {      
        if (language && hljs.getLanguage(language)) {
          try {
            return `<pre><code class="hljs language-${language}">` +
                   hljs.highlight(code, { language  }).value +
                   '</code></pre>';
          } catch (__) {}
        }
    
        return '<pre class="hljs"><code>' + md.utils.escapeHtml(code) + '</code></pre>';
    }
})

export default function MarkdownEdit() {
    const [htmlString, setHtmlString] = useState('')

    const parse = (text: string) => setHtmlString(md.render(text));

    return (
        <div className="markdownEditConainer">
            <textarea 
                className="edit" 
                onChange={(e) => parse(e.target.value)} 
            />
            <div 
                className="show"
                id="write"
                dangerouslySetInnerHTML={{ __html: htmlString }}
            />
        </div>
    )
}

來看一下代碼高亮的效果圖:

代碼高亮效果圖

五、同步滾動

markdown編輯器還有一個重要的功能就是在我們滾動一個區域的內容時,另一塊區域也跟著同步的滾動,這樣才方便查看

接下來我們來實作一下,我會將我實作時踩的坑也一并列出來,讓大家也印象深刻點,免得以后也犯同樣的錯誤

剛開始主要實作思路就是當滾動其中一塊區域時,計算滾動比例(scrollTop / scrollHeight),然后使另一塊區域當前的滾動距離占總滾動高度的比例等于該滾動比例

import React, { useState, useRef, useEffect } from 'react'
import markdownIt from 'markdown-it'
import './theme/github-theme.css'  
import hljs from 'highlight.js'
import 'highlight.js/styles/github.css' 

const md = new markdownIt({
    highlight: function (code, language) {      
        if (language && hljs.getLanguage(language)) {
          try {
            return `<pre><code class="hljs language-${language}">` +
                   hljs.highlight(code, { language  }).value +
                   '</code></pre>';
          } catch (__) {}
        }
    
        return '<pre class="hljs"><code>' + md.utils.escapeHtml(code) + '</code></pre>';
    }
})

export default function MarkdownEdit() {
    const [htmlString, setHtmlString] = useState('')
    const edit = useRef(null)  // 編輯區元素
    const show = useRef(null)  // 展示區元素

    const parse = (text: string) => setHtmlString(md.render(text));

    // 處理區域的滾動事件
    const handleScroll = (block: number, event) => {
        let { scrollHeight, scrollTop } = event.target
        let scale = scrollTop / scrollHeight  // 滾動比例

        // 當前滾動的是編輯區
        if(block === 1) {
            // 改變展示區的滾動距離
            let { scrollHeight } = show.current
            show.current.scrollTop = scrollHeight * scale
        } else if(block === 2) {  // 當前滾動的是展示區
            // 改變編輯區的滾動距離
            let { scrollHeight } = edit.current
            edit.current.scrollTop = scrollHeight * scale
        }
    }

    return (
        <div className="markdownEditConainer">
            <textarea 
                className="edit" 
                ref={edit}
                onScroll={(e) => handleScroll(1, e)}
                onChange={(e) => parse(e.target.value)} 
            />
            <div 
                className="show"
                id="write"
                ref={show}
                onScroll={(e) => handleScroll(2, e)}
                dangerouslySetInnerHTML={{ __html: htmlString }}
            />
        </div>
    )
}

這是我做的時候的第一版,確實是實作了兩塊區域的同步滾動,但是存在兩個bug,來看看是哪兩個

bug1:

這是一個很致命的bug,先埋個伏筆,先來看效果:

初版同步滾動效果圖

同步滾動的效果實作了,但能很明顯得看到,當我手動滾動完以后停止了任何操作,但是兩個區域仍然在不停的滾動,這是為什么呢?

排查了一下代碼,發現 handleScroll 這個方法會無限觸發,假設當我們手動滾動一次編輯區后會觸發其 scroll方法,即會呼叫 handleScroll 方法,然后會去改變「展示區」的滾動距離,此時又會觸發展示區的 scroll方法,即呼叫 handleScroll 方法,然后會去改變「編輯區」的滾動距離 … 就這樣一直回圈往復,才會出現圖中的bug

后來我想了個比較簡單的解決辦法,就是用一個變數記住你當前手動觸發的是哪個區域的滾動,這樣就可以在 handleScroll 方法里區分此次滾動是被動觸發的還是主動觸發的了

import React, { useState, useRef, useEffect } from 'react'
import markdownIt from 'markdown-it'
import './theme/github-theme.css'  
import hljs from 'highlight.js'
import 'highlight.js/styles/github.css'

const md = new markdownIt({
    highlight: function (code, language) {      
        if (language && hljs.getLanguage(language)) {
          try {
            return `<pre><code class="hljs language-${language}">` +
                   hljs.highlight(code, { language  }).value +
                   '</code></pre>';
          } catch (__) {}
        }
    
        return '<pre class="hljs"><code>' + md.utils.escapeHtml(code) + '</code></pre>';
    }
})

let scrolling: 0 | 1 | 2 = 0  // 0: none; 1: 編輯區主動觸發滾動; 2: 展示區主動觸發滾動
let scrollTimer;  // 結束滾動的定時器

export default function MarkdownEdit() {
    const [htmlString, setHtmlString] = useState('')
    const edit = useRef(null) 
    const show = useRef(null)  

    const parse = (text: string) => setHtmlString(md.render(text));

    const handleScroll = (block: number, event) => {
        let { scrollHeight, scrollTop } = event.target
        let scale = scrollTop / scrollHeight  

        if(block === 1) {
            if(scrolling === 0) scrolling = 1;  // 記錄主動觸發滾動的區域
            if(scrolling === 2) return;    // 當前是「展示區」主動觸發的滾動,因此不需要再驅動展示區去滾動

            driveScroll(scale, showRef.current)  // 驅動「展示區」的滾動
        } else if(block === 2) {  
            if(scrolling === 0) scrolling = 2;
            if(scrolling === 1) return;    // 當前是「編輯區」主動觸發的滾動,因此不需要再驅動編輯區去滾動

            driveScroll(scale, editRef.current)
        }
    }

    // 驅動一個元素進行滾動
    const driveScroll = (scale: number, el: HTMLElement) => {
        let { scrollHeight } = el
        el.scrollTop = scrollHeight * scale

        if(scrollTimer) clearTimeout(scrollTimer);
        scrollTimer = setTimeout(() => {
            scrolling = 0    // 在滾動結束后,將scrolling設為0,表示滾動結束
            clearTimeout(scrollTimer)
        }, 200)
    }

    return (
        <div className="markdownEditConainer">
            <textarea 
                className="edit" 
                ref={edit}
                onScroll={(e) => handleScroll(1, e)}
                onChange={(e) => parse(e.target.value)} 
            />
            <div 
                className="show"
                id="write"
                ref={show}
                onScroll={(e) => handleScroll(2, e)}
                dangerouslySetInnerHTML={{ __html: htmlString }}
            />
        </div>
    )
}

這樣就解決了上述的bug了,同步滾動也算很不錯得實作了,現在的效果就跟文章開頭展示的圖片里效果一樣了

bug2:

這里還存在一個很小的問題,也不算是bug,應該算是設計上的思路問題,那就是兩個區域其實還沒完完全全實作同步滾動,先來看看原先的設計思想

img

編輯區和展示區的可視高度是一樣的,但一般編輯區的內容經過markdown渲染后,總的滾動高度是會高于編輯區總的滾動高度的,所以我們無法僅憑scrollTopscrollHeight使得兩個區域同步滾動,比較晦澀,用具體的資料來看一下

屬性編輯區展示區
clientHeight300300
scrollHeight500600

假設我們現在滾動編輯區到最底部,那么此時「編輯區」的 scrollTop 應為 scrollHeight - clientHeight = 500 - 300 = 200,按照我們原本計算滾動比例的方式得出 scale = scrollTop / scrollHeight = 200 / 500 = 0.4,那么「展示區」同步滾動后,scrollTop = scale * scrollHeight = 0.4 * 600 = 240 < 600 - 300 = 300,但事實就是編輯區滾動到最底部了,而展示區還沒有,顯然不是我們要的效果

換一種思路,我們在計算滾動比例時,應計算的是當前的 scrollTopscrollTop最大值的比例,這樣就能實作同步滾動了,仍然用剛才那個例子來看: 此時編輯區滾動到最底部,那么scale應為 scrollTop / (scrollHeight - clientHeight) = 200 / (500 - 300) = 100%,表示編輯區滾動到最底部了,那么在展示區同步滾動時,他的 scrollTop 就變成了 scale * (scrollHeight - clientHeight) = 100% * (600 - 300) = 300,此時的展示區也同步滾動到了最底部,這樣就實作了真正的同步滾動了

來看一下改進后的代碼

import React, { useState, useRef, useEffect } from 'react'
import markdownIt from 'markdown-it'
import './theme/github-theme.css'  
import hljs from 'highlight.js'
import 'highlight.js/styles/github.css'

const md = new markdownIt({
    highlight: function (code, language) {      
        if (language && hljs.getLanguage(language)) {
          try {
            return `<pre><code class="hljs language-${language}">` +
                   hljs.highlight(code, { language  }).value +
                   '</code></pre>';
          } catch (__) {}
        }
    
        return '<pre class="hljs"><code>' + md.utils.escapeHtml(code) + '</code></pre>';
    }
})

let scrolling: 0 | 1 | 2 = 0  
let scrollTimer;  

export default function MarkdownEdit() {
    const [htmlString, setHtmlString] = useState('')
    const edit = useRef(null) 
    const show = useRef(null)  

    const parse = (text: string) => setHtmlString(md.render(text));

    const handleScroll = (block: number, event) => {
        let { scrollHeight, scrollTop, clientHeight } = event.target
        let scale = scrollTop / (scrollHeight - clientHeight)  // 改進后的計算滾動比例的方法

        if(block === 1) {
            if(scrolling === 0) scrolling = 1;  
            if(scrolling === 2) return;    

            driveScroll(scale, showRef.current)  
        } else if(block === 2) {  
            if(scrolling === 0) scrolling = 2;
            if(scrolling === 1) return;    

            driveScroll(scale, editRef.current)
        }
    }

    // 驅動一個元素進行滾動
    const driveScroll = (scale: number, el: HTMLElement) => {
        let { scrollHeight, clientHeight } = el
        el.scrollTop = (scrollHeight - clientHeight) * scale  // scrollTop的同比例滾動

        if(scrollTimer) clearTimeout(scrollTimer);
        scrollTimer = setTimeout(() => {
            scrolling = 0   
            clearTimeout(scrollTimer)
        }, 200)
    }

    return (
        <div className="markdownEditConainer">
            <textarea 
                className="edit" 
                ref={edit}
                onScroll={(e) => handleScroll(1, e)}
                onChange={(e) => parse(e.target.value)} 
            />
            <div 
                className="show"
                id="write"
                ref={show}
                onScroll={(e) => handleScroll(2, e)}
                dangerouslySetInnerHTML={{ __html: htmlString }}
            />
        </div>
    )
}

兩個bug都已經解決了,同步滾動的功能也算完美實作啦,但對于同步滾動這個功能,其實有兩種概念,一種是兩個區域在滾動高度上保持同步滾動;另一種就是右側的展示區域對應左側的編輯區的內容進行滾動,我們現在實作的是前者,后者可以后續作為新功能實作一下~

六、工具列

最后我們就再實作一下編輯器的工具列部分的工具(加粗、斜體、有序串列等等),因為這幾個工具的實作思路都一致,我們就拿 「加粗」 這個工具舉例子,其余的就可以模仿著寫出來了

加粗工具的實作思路:

  • 游標是否選中文字?
    • 是,將選中文字的兩側加上**
    • 否,在游標所在處添加文字**加粗文字**

動圖效果演示:

加粗工具動圖演示

import React, { useState, useRef, useEffect } from 'react'
import markdownIt from 'markdown-it'
import './theme/github-theme.css'  
import hljs from 'highlight.js'
import 'highlight.js/styles/github.css'

const md = new markdownIt({
    highlight: function (code, language) {      
        if (language && hljs.getLanguage(language)) {
          try {
            return `<pre><code class="hljs language-${language}">` +
                   hljs.highlight(code, { language  }).value +
                   '</code></pre>';
          } catch (__) {}
        }
    
        return '<pre class="hljs"><code>' + md.utils.escapeHtml(code) + '</code></pre>';
    }
})

let scrolling: 0 | 1 | 2 = 0  
let scrollTimer;  

export default function MarkdownEdit() {
    const [htmlString, setHtmlString] = useState('')
    const [value, setValue] = useState('')   // 編輯區的文字內容
    const edit = useRef(null) 
    const show = useRef(null)  

    const handleScroll = (block: number, event) => {
        let { scrollHeight, scrollTop, clientHeight } = event.target
        let scale = scrollTop / (scrollHeight - clientHeight)  

        if(block === 1) {
            if(scrolling === 0) scrolling = 1;  
            if(scrolling === 2) return;    

            driveScroll(scale, showRef.current)  
        } else if(block === 2) {  
            if(scrolling === 0) scrolling = 2;
            if(scrolling === 1) return;    

            driveScroll(scale, editRef.current)
        }
    }

    // 驅動一個元素進行滾動
    const driveScroll = (scale: number, el: HTMLElement) => {
        let { scrollHeight, clientHeight } = el
        el.scrollTop = (scrollHeight - clientHeight) * scale  

        if(scrollTimer) clearTimeout(scrollTimer);
        scrollTimer = setTimeout(() => {
            scrolling = 0   
            clearTimeout(scrollTimer)
        }, 200)
    }

    // 加粗工具
    const addBlod = () => {
        // 獲取編輯區游標的位置,未選中文字時:selectionStart === selectionEnd ;選中文字時:selectionStart < selectionEnd
        let { selectionStart, selectionEnd } = edit.current
        let newValue = selectionStart === selectionEnd
                        ? value.slice(0, start) + '**加粗文字**' + value.slice(end)
                        : value.slice(0, start) + '**' + value.slice(start, end) + '**' + value.slice(end)
        setValue(newValue)
    }

    useEffect(() => {
        // 編輯區內容改變,更新value的值,并同步渲染
        setHtmlString(md.render(value))
    }, [value])

    return (
        <div className="markdownEditConainer">
            <button onClick={addBlod}>加粗</button>   {/* 假設一個加粗的按鈕 */}
            <textarea 
                className="edit" 
                ref={edit}
                onScroll={(e) => handleScroll(1, e)}
                onChange={(e) => setValue(e.target.value)}   // 直接修改value的值,useEffect會同步渲染展示區的內容
                value={value}
            />
            <div 
                className="show"
                id="write"
                ref={show}
                onScroll={(e) => handleScroll(2, e)}
                dangerouslySetInnerHTML={{ __html: htmlString }}
            />
        </div>
    )
}

借助這樣的思路,就可以完成其它各種工具的實作了,

在我已經發布的markdown-editor-reactjs (opens new window)中,已經完成了其它工具的實作,想要看代碼的可以去原始碼里看

七、補充

為了保證包的體積足夠小,我將第三方依賴庫markdown主題代碼高亮主題都通過外鏈的形式匯入了

八、最后

一個簡易版的markdown編輯器就實作了,大家可以手動嘗試實作一下,后續我也會繼續發一些教程,對這個編輯器的功能進行擴展

我將代碼都上傳到了 Github倉庫 (opens new window)(希望大家點個?? star),后續擴展一下功能,并作為一個完整的組件發布到npm給大家使用,希望大家多多支持~(其實我已經悄悄發布,但因功能還不是太完善,就不先拿出來給大家使用了,這里簡單放個npm包的地址 (opens new window))

轉載請註明出處,本文鏈接:https://www.uj5u.com/qianduan/286338.html

標籤:其他

上一篇:當下最受歡迎的編輯器,趕緊用起來!

下一篇:一小時入門vue組件(建議收藏)

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • vue移動端上拉加載

    可能做得過于簡單或者比較low,請各位大佬留情,一起探討技術 ......

    uj5u.com 2020-09-10 04:38:07 more
  • 優美網站首頁,頂部多層導航

    一個個人用的瀏覽器首頁,可以把一下常用的網站放在這里,平常打開會比較方便。 第一步,HTML代碼 <script src=https://www.cnblogs.com/szharf/p/"js/jquery-3.4.1.min.js"></script> <div id="navigate"> <ul> <li class="labels labels_1"> ......

    uj5u.com 2020-09-10 04:38:47 more
  • 頁面為要加<!DOCTYPE html>

    最近因為寫一個js函式,需要用到$(window).height(); 由于手寫demo的時候,過于自信,其實對前端方面的認識也不夠體系,用文本檔案直接敲出來的html代碼,第一行沒有加上<!DOCTYPE html> 導致了$(window).height();的結果直接是整個document的高 ......

    uj5u.com 2020-09-10 04:38:52 more
  • WordPress網站程式手動升級要做好資料備份

    WordPress博客網站程式在進行升級前,必須要做好網站資料的備份,這個問題良家佐言是遇見過的;在剛開始接觸WordPress博客程式的時候,因為升級問題和博客網站的修改的一些嘗試,良家佐言是吃盡了苦頭。因為購買的是西部數碼的空間和域名,每當佐言把自己的WordPress博客網站搞到一塌糊涂的時候 ......

    uj5u.com 2020-09-10 04:39:30 more
  • WordPress程式不能升級為5.4.2版本的原因

    WordPress是一款個人博客系統,受到英文博客愛好者和中文博客愛好者的追捧,并逐步演化成一款內容管理系統軟體;它是使用PHP語言和MySQL資料庫開發的,用戶可以在支持PHP和MySQL資料庫的服務器上使用自己的博客。每一次WordPress程式的更新,就會牽動無數WordPress愛好者的心, ......

    uj5u.com 2020-09-10 04:39:49 more
  • 使用CSS3的偽元素進行首字母下沉和首行改變樣式

    網頁中常見的一種效果,首字改變樣式或者首行改變樣式,效果如下圖。 代碼: <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, ......

    uj5u.com 2020-09-10 04:40:09 more
  • 關于a標簽的講解

    什么是a標簽? <a> 標簽定義超鏈接,用于從一個頁面鏈接到另一個頁面。 <a> 元素最重要的屬性是 href 屬性,它指定鏈接的目標。 a標簽的語法格式:<a href=https://www.cnblogs.com/summerxbc/p/"指定要跳轉的目標界面的鏈接">需要展示給用戶看見的內容</a> a標簽 在所有瀏覽器中,鏈接的默認外觀如下: 未被訪問的鏈接帶 ......

    uj5u.com 2020-09-10 04:40:11 more
  • 前端輪播圖

    在需要輪播的頁面是引入swiper.min.js和swiper.min.css swiper.min.js地址: 鏈接:https://pan.baidu.com/s/15Uh516YHa4CV3X-RyjEIWw 提取碼:4aks swiper.min.css地址 鏈接:https://pan.b ......

    uj5u.com 2020-09-10 04:40:13 more
  • 如何設定html中的背景圖片(全屏顯示,且不拉伸)

    1 <style>2 body{background-image:url(https://uploadbeta.com/api/pictures/random/?key=BingEverydayWallpaperPicture); 3 background-size:cover;background ......

    uj5u.com 2020-09-10 04:40:16 more
  • Java學習——HTML詳解(上)

    HTML詳解 初識HTML Hyper Text Markup Language(超文本標記語言) 1 <!--DOCTYPE:告訴瀏覽器我們要使用什么規范--> 2 <!DOCTYPE html> 3 <html lang="en"> 4 <head> 5 <!--meta 描述性的標簽,描述一些 ......

    uj5u.com 2020-09-10 04:40:33 more
最新发布
  • 我的第一個NPM包:panghu-planebattle-esm(胖虎飛機大戰)使用說明

    好家伙,我的包終于開發完啦 歡迎使用胖虎的飛機大戰包!! 為你的主頁添加色彩 這是一個有趣的網頁小游戲包,使用canvas和js開發 使用ES6模塊化開發 效果圖如下: (覺得圖片太sb的可以自己改) 代碼已開源!! Git: https://gitee.com/tang-and-han-dynas ......

    uj5u.com 2023-04-20 07:59:23 more
  • 生產事故-走近科學之消失的JWT

    入職多年,面對生產環境,盡管都是小心翼翼,慎之又慎,還是難免捅出簍子。輕則滿頭大汗,面紅耳赤。重則系統停擺,損失資金。每一個生產事故的背后,都是寶貴的經驗和教訓,都是專案成員的血淚史。為了更好地防范和遏制今后的各類事故,特開此專題,長期更新和記錄大大小小的各類事故。有些是親身經歷,有些是經人耳傳口授 ......

    uj5u.com 2023-04-18 07:55:04 more
  • 記錄--Canvas實作打飛字游戲

    這里給大家分享我在網上總結出來的一些知識,希望對大家有所幫助 打開游戲界面,看到一個畫面簡潔、卻又富有挑戰性的游戲。螢屏上,有一個白色的矩形框,里面不斷下落著各種單詞,而我需要迅速地輸入這些單詞。如果我輸入的單詞與螢屏上的單詞匹配,那么我就可以獲得得分;如果我輸入的單詞錯誤或者時間過長,那么我就會輸 ......

    uj5u.com 2023-04-04 08:35:30 more
  • 了解 HTTP 看這一篇就夠

    在學習網路之前,了解它的歷史能夠幫助我們明白為何它會發展為如今這個樣子,引發探究網路的興趣。下面的這張圖片就展示了“互聯網”誕生至今的發展歷程。 ......

    uj5u.com 2023-03-16 11:00:15 more
  • 藍牙-低功耗中心設備

    //11.開啟藍牙配接器 openBluetoothAdapter //21.開始搜索藍牙設備 startBluetoothDevicesDiscovery //31.開啟監聽搜索藍牙設備 onBluetoothDeviceFound //30.停止監聽搜索藍牙設備 offBluetoothDevi ......

    uj5u.com 2023-03-15 09:06:45 more
  • canvas畫板(滑鼠和觸摸)

    <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>canves</title> <style> #canvas { cursor:url(../images/pen.png),crosshair; } #canvasdiv{ bo ......

    uj5u.com 2023-02-15 08:56:31 more
  • 手機端H5 實作自定義拍照界面

    手機端 H5 實作自定義拍照界面也可以使用 MediaDevices API 和 <video> 標簽來實作,和在桌面端做法基本一致。 首先,使用 MediaDevices.getUserMedia() 方法獲取攝像頭媒體流,并將其傳遞給 <video> 標簽進行渲染。 接著,使用 HTML 的 < ......

    uj5u.com 2023-01-12 07:58:22 more
  • 記錄--短視頻滑動播放在 H5 下的實作

    這里給大家分享我在網上總結出來的一些知識,希望對大家有所幫助 短視頻已經無數不在了,但是主體還是使用 app 來承載的。本文講述 H5 如何實作 app 的視頻滑動體驗。 無聲勝有聲,一圖頂百辯,且看下圖: 網址鏈接(需在微信或者手Q中瀏覽) 從上圖可以看到,我們主要實作的功能也是本文要講解的有: ......

    uj5u.com 2023-01-04 07:29:05 more
  • 一文讀懂 HTTP/1 HTTP/2 HTTP/3

    從 1989 年萬維網(www)誕生,HTTP(HyperText Transfer Protocol)經歷了眾多版本迭代,WebSocket 也在期間萌芽。1991 年 HTTP0.9 被發明。1996 年出現了 HTTP1.0。2015 年 HTTP2 正式發布。2020 年 HTTP3 或能正... ......

    uj5u.com 2022-12-24 06:56:02 more
  • 【HTML基礎篇002】HTML之form表單超詳解

    ??一、form表單是什么

    ??二、form表單的屬性

    ??三、input中的各種Type屬性值

    ??四、標簽 ......

    uj5u.com 2022-12-18 07:17:06 more