Tự viết chorme extension xem ảnh mọi nơi



  • Giới thiệu

    Được lấy ý tưởng từ bài viết Em thèm vếu trên trang tôi đi code dạo. Ở bài viết đó tác giả làm một extension mỗi khi click vào icon của extension thì sẽ show ra ảnh random
    Mình thấy ý tưởng xem vếu khá là hay nên quyết định phát triển thêm cho nó.
    Extension sẽ hiển thị một khung ảnh trong trình duyệtảnh sẽ tự động thay đổi sau mỗi 5 giây và có thể thay đổi kích thước, thêm chủ đề ảnh và di chuyển khung ảnh tự do tùy ý trong trình duyệt(xem ảnh mọi nơi )

    Popup chọn tag cho ảnh hiển thị

    Ảnh có thể thay đổi kích thước và di chuyển tùy ý trong trình duyệt

    Có thể di chuyển và thay đổi kích thước

    Code

    Code khá dài nên mình sẽ comment luôn trong code để các bạn dễ hiểu nhé!

    Cấu trúc thư mục

    Đầu tiên bạn tạo 1 thư mục để chứa các file như trên.

    • css : Chứa các file css
    • html: Chứa các file html như popup và các trang option
    • js/externals :Chứa các file js được sử dụng bởi các trang bên ngoài như trang Popup, các trang option
    • js/internal: Chứa các file js sẽ được inject vào bên trong website
    • js/libraries: Chứa các thư viện của bên thứ ba như Jquery, bootstrap,..
    • logo.png: Icon hiển thị của extension
    • manifest.json: Trái tim của extension, dùng để config mọi thứ cho extension, sẽ được nói rõ ở dưới

    File manifest.json

    manifest.json là file config cho extension, sẽ quyết định xem extension của bạn có những gì. Nội dung sẽ là như sau:

    {
      "manifest_version": 2, //version của chrome extension bạn sử dụng
      "name": "gif everywhere", // tên của extension
      "description": "This extension get a random gif for relaxing", //mô tả extension
      "version": "2.0", //version extension của bạn
      "browser_action": {
        "default_icon": "logo.png", //icon của extension 
        "default_popup": "html/popup.html" //popup hiển thị khi click vào icon
      },
      "permissions": [ //khai báo quyền extension cần sử dụng, và các url mà extension sẽ chạy trên trang có url đó
        "activeTab", //quyền truy cập vào các tab đang hoạt động
        "http://*/*", 
        "https://*/*", //extension sẽ hoạt động trên tất cả các trang web 
        "storage"
      ],
      "content_scripts": [ //inject file js,css.. vào các trang web
        {
          "matches": [ // các trang sẽ được inject - trường hợp này là tất cả cá trang
            "http://*/*",
            "https://*/*"
          ],
          "js": [ //file js được inject 
            "js/libraries/jquery.min.js",
            "js/libraries/jquery-ui.js",
            "js/internal/background.js"
          ],
          "css": [ //file css được inject
            "css/jquery-ui.css",
            "css/background.css"
          ],
          "run_at": "document_end" // file sẽ được inject vào sau khi DOM được tải xong
        }
      ],
      "web_accessible_resources": [ // danh sách các file được inject trực tiếp vào trang web
        "js/internal/background.js"
      ]
    }
    
    

    Popup

    Popup là thành phần html sẽ được hiển thị khi chúng ta click vào biểu tượng của extension, bản chất của nó cũng là một trang html nên chúng ta có thể thỏa mái sử dụng css, js cho thành phần này.

    Thường thì trang popup này sẽ hiển thị thông tin của extension, cho người dùng cài đặt và tương tác với extension.

    Popup sẽ gồm 2 file chính là html/popup.htmlpopup.js, ngoài ra còn có 1 số thư viện bên ngoài như jquery, bootstrap, file css custom cho đẹp.
    nội dung file popup.html

    <!doctype html>
    <html>
    <head>
        <title>Getting Started Extension's Popup</title>
        <meta charset="utf-8" />
        <link rel="stylesheet" type="text/css" href="../css/bootstrap.min.css">
        <link rel="stylesheet" type="text/css" href="../css/popup.css">
        <script type="text/javascript" src="../js/libraries/jquery.min.js"></script>
        <script type="text/javascript" src="../js/externals/popup.js"></script>
    </head>
    <body>
        <div class="container content">
            <div class="input-group">
                <input id="tag" type="text" class="form-control" name="tag" placeholder="Additional Tag">
                <span class="input-group-addon btn">Add Tag</span>
            </div>
            <hr>
            <div class="checkbox-inline">
                <label><input type="checkbox" value="cat">Cat</label>
            </div>
            <div class="checkbox-inline">
                <label><input type="checkbox" value="dog">Dog</label>
            </div>
            <div class="checkbox-inline">
                <label><input type="checkbox" value="animals">Animals</label>
            </div>
            <div class="checkbox-inline">
                <label><input type="checkbox" value="anime">Anime</label>
            </div>
            <hr>
            <div class="my-tags">
            </div>
            <hr>
    
            <div class="btn btn-success" id="play">Play</div>
            <div class="btn btn-warning" id="pause">Pause</div>
      </div>
    </body>
    </html>
    

    File popup.js sẽ làm nhiệm vụ bật, tắt ảnh và thay đổi thẻ tag của ảnh.

    Việc play hay pause ảnh là do script ở file background.js được inject vào các trang web thực hiện, script ở phần popup ở một môi trường hoàn toàn khác nên sẽ không thể trực tiếp chạy script hay lấy dữ liệu của tab hiện tại được, mà phải thông qua chrome.* APIs

    function addTag(tagName) { // sử dụng chrome storage để lưu trữ thẻ tag người dùng nhập thêm
        chrome.storage.sync.get('myTags', function(data) {
            if (Array.isArray(data.myTags)) {
                data.myTags.push(tagName);
            } else {
                data.myTags = [tagName];
            }
            chrome.storage.sync.set({myTags: data.myTags}, function() {
                console.log('The number is set to ' + data);
                renderTag(tagName);
            });
        });
    }
    
    function deleteTag(tagName) { // Xóa bỏ tag từ chrome storage
        chrome.storage.sync.get('myTags', function(data) {
            let id = data.myTags.indexOf(tagName);
            if (id !== -1) {
                data.myTags.splice(id, 1);
            }
            chrome.storage.sync.set({myTags: data.myTags}, function() {
                console.log('The number is set to ' + data);
            });
        });
    }
    
    function renderTag(tagName) { // render checkbox tương ứng với thẻ tag
        $(".my-tags").append(`
            <div class="checkbox-inline">
                <label><input type="checkbox" value="${tagName}">${tagName}</label>
                <span class="delete-tag"> x</span>
            </div>`
        );
    }
    
    function renderAllTag() { // render tất cả các thẻ tag của người dùng
        chrome.storage.sync.get('myTags', function(data) {
            if (Array.isArray(data.myTags)) {
                data.myTags.forEach((item) => {
                    renderTag(item)
                });
            }
        });
    }
    
    document.addEventListener('DOMContentLoaded', function () { 
        renderAllTag();
        $("#pause").hide();
        $('#play').on('click', () => {
            chrome.tabs.executeScript(null, {code:"playImg()"});
    
            $("#pause").show();
            $("#play").hide();
        });
    
        $('#pause').on('click', () => {
            chrome.tabs.executeScript(null,
              {code:"pauseImg()"});
            window.close();
            $("#pause").hide();
            $("#play").show();
        });
    
        chrome.tabs.query({active: true, currentWindow: true}, function(tabs) { // get trang thái pause, play của ảnh trên trang
            chrome.tabs.sendMessage(tabs[0].id, {message: "get status"}, function(response) {
                if (response.isPlaying) {
                    $("#pause").show();
                    $("#play").hide();
                }
                $(".checkbox-inline input").each((index, element) => {
                    let thth = $(element)[0].value;
                    let id = response.tagList.findIndex((tag) => { return tag === $(element)[0].value});
                    if (id >= 0) {
                        $(element).attr('checked', true);
                    }
                });
            });
        });
    
        $(document).on('change', ".checkbox-inline input", (e) => { // thêm hoặc xóa tag khi người dùng click vào checkbox
            let tag = e.target.value;
            let message = '';
            if (e.target.checked) {
                message = 'add tag';
                
            } else {
                message = 'delete tag';
            }
            chrome.tabs.query({active: true, currentWindow: true}, function(tabs) {
                chrome.tabs.sendMessage(tabs[0].id, {message: message, tag: tag});
            });
        })
    
        $(".input-group-addon").on('click', () => { // thêm thẻ tag
            let tag = $('#tag').val();
            if (tag !== '') {
                $('#tag').val('');
                addTag(tag);
            } else {
                console.log('khong nhap gi');
            }
            
        });
    
        $(document).on("mouseover", ".checkbox-inline", function() { //hiển thị nút xóa tag khi hover chuột qua checkbox
            if (!$(this).find('input')[0].checked) {
                $(this).find("span").css('display', 'inline');
            }
        });
        $(document).on("mouseout", ".checkbox-inline", function() {
            $(this).find("span").css('display', 'none');
        });
    
        $(document).on("click", ".delete-tag", function() { // khi người dùng xóa tag
            let tagName = $(this).parent().find("input").val();
            $(this).parent().remove();
            deleteTag(tagName);
        })
    });
    

    Inject file js

    Có thể inject nhiều file vào trong website, trong project này khá đơn giản nên chúng ta chỉ cần inject một file là đủ rồi, file background.js sẽ được inject vào mọi trang mà trình duyệt mở, đoạn script trong file sẽ nhận lệnh từ popup của extension thông qua chrome.* APIs để thực hiện hiển thị ảnh.

    var tagList = ["cat"]; // tag mặc định
    var img = 0; // biến cục bộ để setInterval
    var isPlaying = false; //trang thái ảnh có đang chạy hay không
    
    function renderStatus(statusText) { // hiển thị trạng thái load ảnh
    $('#status').text(statusText);
    }
    
    function renderImage(imageData) { //append url image nhận được vào thẻ html
        $('#image-wrap').attr('href', imageData.url)
        $('#image').attr('src', imageData.image_original_url)
    }
    
    async function getUrl() { // get random url của ảnh
        let tag = tagList[Math.floor(Math.random()*tagList.length)];
        if (tag === undefined) {
            return;
        }
        renderStatus('Loading for ' + tag + ' ...');
        let url = 'https://api.giphy.com/v1/gifs/random?api_key=6Mlyf4s706n6UOGc8MxfbIJIMXpLwj4i&tag=' + tag;
        let result = await fetch(url);
        let jsonResult =  await result.json();
        await renderImage(jsonResult.data);
        renderStatus('');
    }
    
    var textnode = '\
        <div id="wrap-extension">\
            <div id="content1" style="display: none;">\
                <a id="image-wrap" href="" target="_blank"><img width="100%" id="image" /></a>\
                <div id="status"></div>\
                <br/>\
            </div>\
        </div>';
    
    function playImg() { // thực hiện play ảnh
        $("#content1").show();
        getUrl();
        img = setInterval(() => {
            getUrl();
        }, 5000);
        isPlaying = true;
    }
    
    function pauseImg() { // dừng hiển thị
        $("#content1").hide();
        console.log(img);
        clearInterval(img);
        isPlaying = false;
    }
    
    $(document).ready(function(){
    
        $("body").append(textnode);
    
        $("#head1").on("click", function(ev) {
            var content = $("#content1");
            if (content.css('display') === "none") {
                content.show();
                playImg();
            } else {
                pauseImg();
            }
        });
        
        $('head').prepend('<script>isVip=true;</script>');
        $('#wrap-extension').draggable({ containment: "window" }).resizable({handles: 'e, w'});
        chrome.runtime.onMessage.addListener((request, port, sendResponse) => {
            switch (request.message) {
                case 'get status':
                    sendResponse({tagList: tagList, isPlaying: isPlaying});
                    break;
                case 'add tag':
                    tagList.push(request.tag);
                    console.log(tagList, 'thêm')
                    break;
                case 'delete tag':
                    let id = tagList.indexOf(request.tag);
                    console.log(id)
                    if (id !== -1) {
                        tagList.splice(id, 1);
                    }
                    console.log(tagList, 'xóa')
            }
        })
    
    });
    

    Kết

    Vậy là chúng ta đã có một extension xem ảnh động ngay trong các website khác
    Tuy nó hoạt động nhưng code vẫn còn khá smell, mong được sự góp ý của các bạn

    Link github: https://github.com/ththth0303/ext các bạn có thể tải về và dùng thử

    Tham khảo:



Có vẻ như bạn đã mất kết nối tới LaptrinhX, vui lòng đợi một lúc để chúng tôi thử kết nối lại.