action icon indicating copy to clipboard operation
action copied to clipboard

My Story / 我的故事

Open cssmagic opened this issue 11 years ago • 4 comments

Video / 视频

  • https://www.bilibili.com/video/BV1p541127JF/

Slides / 幻灯片

此分享的幻灯片实验性地采用了纯 JS 代码的形式。





// MY STORY ABOUT CLICK-EVENT-BINDING

// @CSSMAGIC










// BACKGROUND










// ONE DAY...

// REQUIREMENT: LUCKY DRAW?









var btn = document.getElementById('lucky-draw')

btn.onclick = function () {
    BX.luckyDraw()
}










// TRACKING?










btn.onclick = function () {
    BX.luckyDraw()
    BX.track('lucky-draw')
}










// BUT...








btn.onclick = function () {
    BX.luckyDraw()
}

// some code...

btn.onclick = function () {
    BX.track('lucky-draw')
}





btn.addEventListener('click', function () {
    BX.luckyDraw()
}, false)

// some code...

btn.addEventListener('click', function () {
    BX.track('lucky-draw')
}, false)







// A/B TEST?

// HALF OF USERS HAVE NO BTN?









var btn = document.getElementById('lucky-draw')

// A FIFTY-FIFTY CHANCE:
btn === null






if (btn) {
    btn.addEventListener('click', function () {
        BX.luckyDraw()
    }, false)
}

// some code...

if (btn) {
    btn.addEventListener('click', function () {
        BX.track('lucky-draw')
    }, false)
}







// "USE JQUERY, YOU IDIOT!"








var $btn = $('#lucky-draw')
$btn.on('click', function () {
    BX.luckyDraw()
})

// some code...

$btn.on('click', function () {
    BX.track('lucky-draw')
})








// SO FAR SO GOOD!

// BUT...










// SPA?

// HTML LAZY LOAD?

// HTML RENDERED BY JS?







$('.tabs > .surprise').on('click', function () {
    var htmlTab = [
        '<div>',
            '<button id="lucky-draw">Lucky Draw</button>',
        '</div>'
    ].join('')
    $('.tab-panels > .surprise').html(htmlTab)

    // EVENT-BINDING HERE...
})








// WHAT A MESS...











// EVENT DELEGATION?










$('body').on('click', '#lucky-draw', function () {
    BX.luckyDraw()
})










// WHAT IF MORE AND MORE BTNS?








$body = $('body')
$body.on('click', '#lucky-draw', function () {
    BX.luckyDraw()
})


$body.on('click', '#some-btn', function () {
    // do something...
})
$body.on('click', '#another-btn', function () {
    // do something else...
})
// ...








// NEED TO IMPROVE THIS!











// GROUP ALL THESE EVENT DELEGATION?








// FIRST, THE SAME CLASS NAME.

var btn1 = '<button class="action" id="lucky-draw">Lucky Draw</button>'
var btn2 = '<button class="action" id="some-action">Button</button>'
var link1 = '<a href="#" class="action" id="another-action">Link</a>'
var link2 = '<a href="#" class="action" id="another-action-2">Link</a>'





// THEN, ONLY ONE LISTENER.

$body.on('click', '.action', function () {

    // WHEN CLICK ANY '.action', WE COME HERE.

})




$body.on('click', '.action', function () {

    // DISPATCH ACTION
    // USE `id` TO IDENTIFY EACH BTN...

    switch (this.id) {
        case 'lucky-draw':
            BX.luckyDraw()
            break
        case 'some-btn':
            // do something...
            break
        // ...
    }

})







// `ID`?











// WHY NOT HTML5 DATASET?








// USE A CUSTOM DATA ATTRIBUTE -- 'data-action'!

var btn1 = '<button class="action" data-action="lucky-draw">Lucky Draw</button>'
var btn2 = '<button class="action" data-action="some-action">Button</button>'
var link1 = '<a href="#" class="action" data-action="another-action">Link</a>'
var link2 = '<a href="#" class="action" data-action="another-action-2">Link</a>'








// WAIT, CLASS NAMES ARE UNNECESSARY!








// SIMPLIFIED!

var btn1 = '<button data-action="lucky-draw">Lucky Draw</button>'
var btn2 = '<button data-action="some-action">Button</button>'
var link1 = '<a href="#" data-action="another-action">Link</a>'
var link2 = '<a href="#" data-action="another-action-2">Link</a>'




$body.on('click', '[data-action]', function () {
    var actionName = $(this).data('action')

    switch (actionName) {
        case 'lucky-draw':
            BX.luckyDraw()
            break
        case 'some-btn':
            // do something...
            break
        // ...
    }
})







// NO UGLY 'switch'!







var actionList = {
    'lucky-draw': function () {
        BX.luckyDraw()
    },
    'some-btn': function () {
        // do something...
    }
    // ...
}

$body.on('click', '[data-action]', function () {
    var actionName = $(this).data('action')
    var action = actionList[actionName]

    if ($.isFunction(action)) action()
})







// DEFINE MORE ACTION LATER?








// HTML
$body.append('<a href="#" data-action="more">Link</a>')

// JS
$.extend(actionList, {
    'more': function () {
        // ...
    }
})







// MAYBE THIS FITS MORE PEOPLE...

// OPEN-SOURCE IT!








// `ACTION`
// EASY AND LAZY SOLUTION FOR CLICK-EVENT-BINDING.


// GITHUB: CSSMAGIC/ACTION





// API

// DEFINE ACTIONS
action.add({
    'my-action': function () {
        // ...
    }
    // ...
})

// TRIGGER ACTION MANUALLY
action.trigger('my-action')





// WHO'S USING IT?


// CMUI USES `ACTION` AS A CORE SERVICE.
// GITHUB: CMUI/CMUI







// EVERYONE USING CMUI IS USING `ACTION`:

// - M.BAIXING.COM
// - M.VICHY.COM.CN
// - M.UEMALL.COM









// USE CASE: CMUI







CMUI.dialog = {
    template: [
        '<div class="dialog">',
            '<a href="#" data-action="close-dialog">×</a>',
            '<h2><%= data.title %></h2>',
            '<div class="content"><%- data.html %></div>',
        '</div>'
    ].join(''),

    init: function () {
        action.add({
            'close-dialog': function () {
                $(this).closest('.dialog').hide()
            }
        })
    },
    open: function (config) {
        var html = render(this.template, config)
        $(html).appendTo('body').show()
    }
}







// NO EVENT-BINDING CODE NEEDED!










// Q & A

// THX!





  • 幻灯片内的所有代码均为示意代码。
  • 关于 Action 的实际实现,请参阅文档和源码。

cssmagic avatar Dec 04 '14 09:12 cssmagic

很高兴,其实我一直也会这么写,并且会为actionList里的每一个属性(action)重新指定上下文及event; 类似于:

actionList[actionName] && actionList[actionName].call(this,event);

好处是: 我们会有一些场景需要操作触发该action的元素; 坏处是: trigger的时候,[context]就会出错,而且代码阅读起来会比较混乱;

请问有没有更好的方式解决这个问题?比如trigger的时候自动绑定正确的上下文?

litson avatar Dec 21 '14 15:12 litson

@litson 谢谢留言。

trigger的时候,[context]就会出错,而且代码阅读起来会比较混乱; 请问有没有更好的方式解决这个问题?比如trigger的时候自动绑定正确的上下文?

Action 目前的设计可以参考这里(#19),最后一节讨论了 .trigger() 方法的使用问题。

cssmagic avatar Dec 22 '14 06:12 cssmagic

个人觉得Backbone里面的event map也比较方便

events: {
  'click [data-action="my-action"]': 'doMyAction'
}

而且上下文是自动绑定

anhulife avatar Dec 23 '14 02:12 anhulife

@anhulife 谢谢评论。

Backbone 的这种机制自然是极好的,但不是每个项目都会使用或适合使用 Backbone。Action 专注做这一件事,而且它要解决的问题十分通用,使得它适用于大多数项目。而且 Action 的体积十分小巧,minify + gzip 后只有 500 多字节,对任何项目来说几乎都是零负担。

关于 “上下文自动绑定”,Action 也是这样设计的。参见 #19。

cssmagic avatar Dec 23 '14 07:12 cssmagic