iT邦幫忙

7

[JavaScript] 步驟紀錄器實作

第一次發技術文,這篇文章原文寫在我的Blog上,因為這邊不能直接寫Demo所以我改給JS Fiddle的Demo連結。

以下正文開始

前言

前天有人問了一題有趣的題目,內容是問要怎麼實作編輯器得上一步下一步動作,也是就是說要做一個步驟紀錄器,覺得沒做過這種東西覺得好像蠻有趣的,就來試試看。

步驟紀錄器原理

基本原理

原理很簡單,先建立一個Array當作容器,每次輸入時使用就把資料塞進Array中,在使用一個變數紀錄當前的步驟,上一步就是把資料還原上一個Array元素的內容而已,是不是很簡單呢?XD

紀錄的頻率

HTML的DOM事件只能監控你每次輸入的瞬間,假如說你輸入123,這樣會產生3步,這樣感覺紀錄的太細膩,所以我們要做一個延遲,需要輸入後停止多久的時間,紀錄器才會把資料記錄下來。

紀錄器的空間控管

當然紀錄器的空間要有限制,不是一直Array.push()就好,這樣你會害使用者記憶體爆掉的....

所以我們一定要限制Array的大小,對使用者來說就是可以紀錄的幾個步驟。

步驟紀錄器實作

建立基本容器與變數

實作使用TextArea來當示範,首先先建立2個ButtonTextArray

<button id="prev" type="button">上一步</button>
<button id="next" type="button">下一步</button>
<textarea id="text" cols="5"></textarea>

建立Array和使用的變數

var step = 0; // 步驟變數
var textList = new Array(); // array容器
var prev = document.getElementById('prev');
var next = document.getElementById('next');
var text = document.getElementById('text');

步驟事件建立及加入輸入的監聽

寫一個事件,每個步驟都會把TextArea的內容寫入textList,並寫步驟+1

function textWrite(){
    step++; 
    textList.push(text.value);
}

當然只有事件是不會自動執行的,此時我們需要監控TextArea的輸入,並執行上面的事件

text.addEventListener('input', textWrite);

上下步事件建立

這邊開始就簡單了,上一步只要讀取上個Array元素的資料並塞進TextArea,下一步則是倒過來。

prev.onClick = function(){
    // 判斷是否還有上一步
    if(step < 2){
        alert('沒有上一步');
    }else{
        step--;// 步驟往回一步
        text.value = textList[step - 1]; // 因為Array從0開始所以要-1
    }
}

next.onClick = function(){
    if(step => textList.length){
        alert('沒有下一步');
    }else{
        step++;
        text.value = textList[step - 1];
    }
}

Demo連結

延遲紀錄的實現

這個就要使用我的上一篇文章,使用Lodash的_.debounce就能實現了。

var debounce = _.debounce(textWrite, 500);// 延遲500毫秒紀錄
text.addEventListener('input', debounce);// 改成監聽延遲函數

Demo連結

步驟器的限制

要限制很簡單,只要儲存容器本身要限制寬度就行,這邊主要的另一個問題應該是超出範圍是該怎麼辦?
答案很簡單就是把現有資料向前移動就行,然後在最後一項資料再塞進新資料。

var textList = new Array(3); // 限制Array只能4個元素
function textWrite(){
    // 判斷是否已到最大紀錄步驟
    if(step === textList.length){
        for(var i = 0;i < textList.length - 1; i++){
            textList[i] = textList[i + 1];
        }
        textList[step - 1] = text.value; 
    } else {
        step++; 
        textList[step] = text.value;
    }
}

Demo連結

後記

整體來說不難,最後一項步驟器的限制難度有比較高,我在想資料位移時還一直想到腦筋打結,原本是for遞減,後來想一想好像遞增比較好寫,
害我卡了很久,不過很多IT邦很多大大說編輯器很難寫是真的,雖然基本的觀念不是很難,但是細部的微調真的很難寫,例如我其時還少寫了一個步驟不在最後一步時,
要覆寫後面的步驟,實作起來應該是更難,我這邊就不做了(暈)。

Blog原文


2 則留言

2
fysh711426
iT邦新手 1 級 ‧ 2018-09-09 23:29:56

原來 Lodash 有 _.debounce 這麼好用的東西!!

接續文章結尾,完成了 覆寫後面 的部分,並稍做修改將陣列做成環狀佇列,這樣滿了以後就不需要每次位移。

var prev = document.getElementById('prev');
var next = document.getElementById('next');
var text = document.getElementById('text');

var size = 6;    //佇列大小,需空一個位置給佇列結尾
var front = 0;   //佇列開頭
var real = 1;    //佇列結尾的下一個位置
var curr = 0;    //步驟當前位置
var queue = new Array(size); //佇列容器
queue[0] = "";   //設定第一個元素為空白

prev.onclick = function() {
	//判斷是不是在開頭位置
    if (curr === front){
        alert('沒有上一步');
        return;
    }
    //將當前位置移動到上一步
    curr = (curr + size - 1) % size;
    text.value = queue[curr];
}
next.onclick = function() {
	//判斷是不是在結尾的位置
	var next = (curr + 1) % size;
    if (next === real){
        alert('沒有下一步');
        return;
    }
    //將當前位置移動到下一步
    curr = next;
    text.value = queue[curr];
}
text.addEventListener('input', function textWrite() {
    //判斷當前位置是不是在結尾
    var next = (curr + 1) % size;
    if (next === real) {
    	//判斷佇列是否已滿
    	if ((real + 1) % size === front) {
        	//已滿則覆蓋開頭的紀錄
        	front = (front + 1) % size;
        }
    }
    //設到當前位置到下個位置上
    curr = next;
    //將文字加入佇列
    queue[curr] = text.value;
    //將結尾設到當前位置的下個位置上(覆寫後面)
    real = (curr + 1) % size;
});

Demo


結構圖:

https://ithelp.ithome.com.tw/upload/images/20180910/20106865TXbOyVWePQ.jpg


補充:
後來想想其實並不能叫環狀佇列,因為沒有 先進先出 的概念,但循環的方式非常像,讓我很直覺的聯想到環狀佇列。
/images/emoticon/emoticon13.gif

Homura iT邦研究生 5 級‧ 2018-09-09 23:45:47 檢舉

喔喔~~
太棒了明天早上來看看

Homura iT邦研究生 5 級‧ 2018-09-10 09:34:05 檢舉

看懂了
真的省略了覆蓋步驟耶/images/emoticon/emoticon25.gif
謝謝分享/images/emoticon/emoticon12.gif

還有queue[0] = "";改成queue[0] = text.value;比較好

哈對,[0] 用來放 textbox 的預設值。

0
partyyaya
iT邦新手 5 級 ‧ 2018-09-10 08:53:11

感謝樓主

Homura iT邦研究生 5 級‧ 2018-09-10 09:29:49 檢舉

因為Array是從0開始
其實也能寫成

text.value = textList[step];
step++;

只是我原本的寫法感覺比較直覺/images/emoticon/emoticon01.gif

我要留言

立即登入留言