Share
Trang chủ / Tất cả hướng dẫn / Lập trình windows / Viết chrome extension hỗ trợ fullscreen

Viết chrome extension hỗ trợ fullscreen

Video hướng dẫn viết chrome extension hỗ trợ chức năng fullscreen cho trình duyệt google chrome, sử dụng kiến thức về fullscreen API

Như đã nói trong hướng dẫn trước, ngày hôm nay, chúng ta sẽ bắt đầu ứng dụng html5 fullscreen api vào các mục đích thực tế. Chính vì vậy, đây là một hướng dẫn áp dụng thực tế, không phải hướng dẫn cơ bản, vì vậy, nếu bạn chưa biết các kiến thức cơ bản đó, hãy xem lại các bài viết trước nhé. Còn bây giờ, chúng ta bắt đầu thôi.

Giới thiệu

Nhắc lại, Fullscreen API là một javascript api được cung cấp trong mô tả của HTML5, cho phép đưa các element vào chế độ fullscreen. Rất ngắn gọn, đơn giản. Và chúng ta phải gọi các phương thức API này ở một “vị trí” có thể “nhìn thấy các element” này, đó là ngay trong document của trang html.

Tính năng

  1. Fullscreen toàn bộ website.
  2. Chỉ fullscreen một element, lựa chọn element
  3. Shortcut key.

Giải pháp

Tính năng thứ nhất : Fullscreen toàn bộ document hiện tại.

Mô tả : Khi ấn vào page action, thì toàn bộ tab hiện tại sẽ fullscreen

Các bạn biết đấy, sự kiện page action được xử lý ở background.js. nhưng ở vị trí này, chúng ta lại không thể “nhìn thấy” các element trong trang web được. Còn các phương thức fullscreen thì phải được gọi ở nơi có thể thấy được các element, điều đó có nghĩa là các đoạn script phải được chèn vào trang. Thật may, Chrome cung cấp hai cách để chèn script là “content script” và  chrome.tabs.executeScript, Đồng thời cũng cung cấp API để có thể giao tiếp giữa các đoạn script này và background.js là chrome.extension.onMessage

Như vậy các bạn hình dung được giải pháp rồi chứ. Click vào Page action, background.js sẽ excute một đoạn script vào tab hiện tại.

Tính năng thứ hai : Fullscreen một element.

Mô tả : Đầu tiên, người dùng cần chọn element, sau đó click vào element thì nó sẽ fullscreen

Để người dùng chọn element, thì ta phải đưa họ từ trạng thái duyệt web bình thường về trang thái lựa chọn element, Chúng ta có hai cách để làm chuyện này, là context menu và shortcut key . Sau đó chúng ta sẽ highlight element tại vị trí mà họ move chuột. Để xác địch chọn element, họ click chuột. Như vậy, chúng ta phải handle ba sự kiện mouseover, mouseoutclick . Khi mouseover để đâu thì highlight tới đó, mouseout ra thì tắt highlight, clickthì sẽ gọi fullscreen.

Tính năng này khó và phức tạp hơn tính năng thứ nhất, chúng ta không biết element nào sẽ được fullscreen, mà phải hiện thực thêm các tính năng phụ, cho phép người dùng lựa chọn element, lại còn phải giao tiếp với background script để cập nhật lại context menu, và shortcut key

Tính năng thứ ba : Shortcut key

Mục đích chính là tạo sự tiện dụng, thay vì phải ấn vào context menu hoặc page action, họ ấn tổ hợp phím :

  • Ctrl + Shift + S : Lựa chọn element để fullscreen
  • Ctrl + Shift + F : Fullscreent toàn bộ document hiện tại

Manifest

Vẫn như các extension bình thường khác, chúng ta cần khai báo background.js, contentScript.js, page action, permissions. Đặt biệt đáng nói ở đây là một khai báo mới, khai báo command, Mục đích là để cho chrome biết chúng ta muốn sử dụng shortcut key nào.

"commands": {
    "beginselectElement": {
        "suggested_key": {
            "default": "Ctrl+Shift+S",
            "windows": "Ctrl+Shift+S",
            "mac": "Command+Shift+S"
        },
        "description": "Send a 'toggle-feature' event to the extension"
    },
    "_execute_page_action": {
        "suggested_key": {
            "default": "Ctrl+Shift+F",
            "windows": "Ctrl+Shift+F",
            "mac": "Command+Shift+F"
        }
    }
}

Chúng ta handle hai shortcut là Ctrl+Shift+S để bật trạng thái select element, và Ctrl+Shift+F để fullscreen toàn bộ document.

Trong phần permission, cần phải khai báo một quyền là contextMenu để có thể thêm các context menu item:

"permissions": [
    "tabs",
    "activeTab",
    "contextMenus",
    "http://*/*",
    "https://*/*"
],

Chú ý là bạn cũng cần khai báo icon kích thước 16px để làm icon cho context menu item:

"16": "images/icon_16.png"

Page action

Khi ấn vào page action, sẽ không có popup nào cần hiện, nên ta không cần khai báo popup trong manifest

"page_action": {
    "default_icon": "images/icon_19.png",
    "default_title": "__MSG_pageAction__"
}

Khi sự kiện chrome.pageAction.onClicked được gọi, Ta excute một đoạn script ngắn, yêu cầu fullscreen cho document hiện tại trong tab

chrome.pageAction.onClicked.addListener(function (tab) {
    chrome.tabs.executeScript(tab.id, {code: "document.documentElement.webkitRequestFullScreen(Element.ALLOW_KEYBOARD_INPUT);"});
});

Context menu và ContentScript

Thêm context menu item

Thêm một context menu item, item này sẽ hiển thị khi người dùng ấn chuột phải vào bất cứ thành phần nào trên website:

var pageContext = chrome.contextMenus.create({
        "id": "ext_fullscreen",
        "title": chrome.i18n.getMessage('selectElement'),
        "contexts": ["all"]
    }
);

Ta còn cần phải handle sự kiện click của nó :

chrome.contextMenus.onClicked.addListener(
    function (info, tab) {
        chrome.tabs.sendMessage(tab.id, {action: "beginselectElement"}, function (response) {
            updateContextMenu(response.isSelecting);
        });
    }
);

Trong Extension này, chúng ta chỉ thêm một menu item duy nhất, nên không cần phải kiểm tra xem item nào được click, mà chỉ cần xử lý thôi. Khi item được click, ta sẽ yêu cầu content Script chuyển người dùng về trạng thái “lựa chọn element” bằng cách gửi một message đến tab có context menu item được ấn. Nội dung gửi đi có định dạng Json, có thuộc tính action là “beginselectElement”.

Sau khi gửi message, contextScript sẽ hồi đáp lại cho mình biết trạng thái hiện tại của tab là gì, dựa vào đó ta thay đổi text của menu item:

//phương thức để thanh đổi text của context menu
var updateContextMenu = function (isSelecting) {
    if (isSelecting == true) {
        chrome.contextMenus.update(pageContext, {
            "title": chrome.i18n.getMessage('cancelSelection')
        });
    }
    else if (isSelecting == false) {
        chrome.contextMenus.update(pageContext, {
            "title": chrome.i18n.getMessage('selectElement')
        });
    }

}

Giao tiếp giưa background và context script

Giờ hãy xem ta handle message này như thế nào ở file contextScript.js nhé:

chrome.extension.onMessage.addListener(function (request, sender, sendResponse) {
    if (request.action == "beginselectElement") {
        if (isSelecting == false) {
            turnOnSelecting();
        }
        else {
            turonOffSelecting();
        }
    }
    sendResponse({isSelecting: isSelecting});
});

Khi nhận được message, hãy kiểm tra xem đó có phải là message dùng để chuyển trạng thái của người dùng hay không, chỉ chuyển khi đúng action là “beginselectElement” thôi nhé. Đồng thời cũng phải kiểm tra trạng thái hiện tại là gì nữa.

Sau khi xử lý các sự kiện xong, ta hồi đáp lại cho background trạng thái hiện tại của tab là gì, để cho background update lại phần text của context menu item

Chuyển trạng thái người dùng và handle sự kiện mouse

Sau khi nhận được message rồi thì làm gì ? Tất nhiên là chuyển trạng thái người dùng rồi. trạng thái được lưu trong biến isSelecting . Biến này rất quan trọng, nếu không có nó, ta sẽ không biết được khi nào nên chuyển về trạng thái nào.

Tại sao phải làm như vậy ? sao không lưu ở background và gửi về contentScript kèm theo message. Bạn lưu ý rằng, file background.js là file có “tầm view” rộng, nó dùng cho tất cả các tab, nhưng trạng thái của mỗi tab thì lại khác nhau ( logic ), vì vậy cũng ta không thể lưu giá trị của trạng thái ( isSelecting ) ở file background được mà phải lưu trong contextScript.js. Thực ra ta cũng có thể lưu trong background.js, nhưng như vậy vô tình làm mọi thứ thêm phức tạp và rất rối, cũng không logic.

//Handle các listener
function turnOnSelecting() {
    isSelecting = true;
    document.addEventListener("mouseover", ononmouseover, false);
    document.addEventListener("mouseout", onmouseout, false);
    //Backup lại sự kiện
    onclickBackup = document.body.onclick;
    document.body.onclick = onmouseclick;
}

//phục hồi
function turonOffSelecting() {
    isSelecting = false;
    if (clickedEl) {
        classie.removeClass(clickedEl, "highlight_fullscreen");
    }
    document.removeEventListener("mouseover", ononmouseover);
    document.removeEventListener("mouseout", onmouseout);
    document.body.onclick = onclickBackup;
}

Không có gì đặt biệt trong hàm trên, ngoài việc đối với sự kiện click, thay vì dùng các phương thức addEventListener và removeEventListener thì ta gán luôn giá trị cho nó. Vì mình đã thử sử dụng các phương thức đó rồi, nhưng nó luôn bị chiếm bởi các event khác, đồng thời cũng có nhiều trường hợp không handle được event trên các element con. Ngoài ra, trong trạng thái này, người dùng chỉ có một mục đích duy nhất là chọn element, nên ta remove hết tất cả các click event.

Tiếp theo chúng ta sẽ handle sự kiện mouse:

//các hàm xử lý sự kiện chuột
var ononmouseover = function (event) {
    clickedEl = event.target;
    classie.addClass(clickedEl, "highlight_fullscreen");
    return true;
}
var onmouseout = function (event) {
    clickedEl = event.target;
    classie.removeClass(clickedEl, "highlight_fullscreen");
    return true;
}
var onmouseclick = function (event) {
    if (event.button == 0) {
        turonOffSelecting();
        chrome.runtime.sendMessage({action:"enterFullscreen",isSelecting: isSelecting}, function(response) {});
        clickedEl.webkitRequestFullScreen(Element.ALLOW_KEYBOARD_INPUT);
        clickedEl = null;
    }
    return false;
}

Như các bạn thấy, khi người dùng move mouse của họ lên một element, chúng ta sẽ add highlight class vào nó để đánh dấu cho người dùng biết là họ đang chọn element nào, khi họ move ra khỏi element đó, ta remove class kia đi. nếu họ ấn chuột và nhả chuột ra, ta kiểm tra, nếu button họ ấn là chuột trái ( có code là 1 ), thì ta tiến hành fullscreen nó, Đồng thời báo cho background script biết để cập nhật lại context menu ( trước đó context menu bị đổi text thành “Cancel selection” ).

Styles

Như đã nói trong hướng dẫn trước, Fullscreen API cung cấp một state mới là : :-webkit-full-screen cho phép tự chúng ta định dạng cho element khi nó fullscreen.

.highlight_fullscreen{
    box-sizing: border-box;
    border:1px solid red;
}
:-webkit-full-screen {
    width: 100%;
    height: 100%;
    overflow: scroll;
}

Bạn chú ý là phải đặt overflow là scroll nhé.

 Lời kết

Vậy đấy, việc áp dụng kiến thức đã học trước đó vào các dự án hiện tại sẽ có thể củng cố và cho các kiến thức đó thể hiện giá trị ở hiện tại, đồng thời cũng giúp cho chúng ta không ngừng sáng tạo, không bị khô cứng trong những kiến thức thô. Đem lại giá trị ở hiện tại cho xã hội.