Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

My Story / 我的故事 #11

Open
cssmagic opened this issue Dec 4, 2014 · 4 comments
Open

My Story / 我的故事 #11

cssmagic opened this issue Dec 4, 2014 · 4 comments
Labels

Comments

@cssmagic
Copy link
Owner

cssmagic commented Dec 4, 2014

Video / 视频

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 cssmagic added the doc label Dec 4, 2014
@litson
Copy link

litson commented Dec 21, 2014

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

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

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

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

@cssmagic
Copy link
Owner Author

@litson 谢谢留言。

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

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

@anhulife
Copy link

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

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

而且上下文是自动绑定

@cssmagic
Copy link
Owner Author

@anhulife 谢谢评论。

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

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

3 participants