【问题标题】:Pass javascript function as data-* attribute and execute将 javascript 函数作为 data-* 属性传递并执行
【发布时间】:2019-04-18 06:41:46
【问题描述】:

在为onClick 属性定义value 时,我们知道如下语法:

<button type="submit" onclick="alert('hi');"></button>
<button type="submit" onclick="doWork"></button> <!-- This one doesn't work -->
<button type="submit" onclick="doWork()"></button>
<button type="submit" onclick="doWork('Mike', 2)"></button>

我感兴趣的是定义一个自定义的data-attribute 并执行如下值:

<button type="submit" data-callback="alert('hi');"      class="marker"></button>
<button type="submit" data-callback="doWork"            class="marker"></button>
<button type="submit" data-callback="doWork()"          class="marker"></button>
<button type="submit" data-callback="doWork('Mike', 2)" class="marker"></button>

<script type="text/javascript">
    jQuery("body").on("click","button.marker", function(e) {
        var callback = jQuery(e.currentTarget).data("callback");

        // Now I need to execute the callback no matter of the format
        // 1. Execute as function's body
        // 2. Or by function 'name'
        // 3. Or by function 'name' with 0 parameters
        // 4. Or by function 'name' with n parameters
    })

    function doWork(name, nr){
        var personName = name || "Unnamed";
        var personNr = nr || 0;
        alert("Name is: " + personName + " Nr: " + personNr);
    }
</script>

我已将示例粘贴到jsBin

如何使用自定义数据属性实现相同的行为?

【问题讨论】:

  • 你可以看看devileval()
  • 避免动态绑定有什么原因吗?会简化问题。
  • 除非你有很好的理由/只是在试验,否则在数据属性中编写代码似乎是个坏主意!您可以在按钮上设置回调 $(".marker").myCallback = doWork;如果您不同意,请随意拓宽我的思路:)
  • @Taplar 是的。样本被简化。在生产代码中,回调意味着由其他组件调用,并且在以后调用。
  • @Mvision : AngularJS 并不认为在属性中编写代码是一个坏主意 :) 它是它的核心,恕我直言,甚至它的 方式 比 jQuery 更好用.

标签: javascript jquery custom-data-attribute


【解决方案1】:

一种方法是使用eval()

jQuery(".container").on("click", "button.marker", function (e) {
    var callback = jQuery(e.currentTarget).data("callback");

    var x = eval(callback)
    if (typeof x == 'function') {
        x()
    }
});

演示:Fiddle

注意:确保它在您的环境中是安全的,即不会因为用户输入错误而导致脚本注入

【讨论】:

  • 我相信eval("doWork('Mike', 2)") 会自行启动该功能。无需通过添加更多() 来重新启动它。
  • @JeremyThille 是 data-callback="doWork" 传递函数引用的情况
  • 我同意,但我猜 OP 正在寻找一种或另一种解决方案,他们会选择"dowork""dowork()" 语法,使if (typeof x == 'function') 测试无用。
  • @JeremyThille 我这样做是因为Or by function 'name' - 我认为 OP 的意思是传递必须执行的函数引用(名称)
  • @ArunPJohny 即使 JeremyThille 的答案更快。我会将您的标记为已接受,因此对于未来的 SO 用户来说“更完整”。谢谢
【解决方案2】:

我认为更好的办法是动态绑定事件并触发它们。如果您希望它们只被其他代码知道,您可以使用自定义事件。

<button type="submit" class="marker marker1"></button>
<button type="submit" class="marker marker2"></button>
<button type="submit" class="marker marker3"></button>
<button type="submit" class="marker marker4"></button>

<script>
    var $markers = $('.marker');
    $markers.filter('.marker1').bind('customCallback', function(){ alert("hi"); });
    $markers.filter('.marker2').bind('customCallback', function(){ doWork(); });
</script>

然后您的其他组件可以使用 $(selector).trigger('customCallback'); 调用它们

【讨论】:

  • 那不是必须是带点的 .filter('.marker1') 吗? :)
【解决方案3】:

只需:

 $("body").on("click","button.marker", function(e) {
     eval($(this).data("callback"));
 })

【讨论】:

  • 如何传递参数?
  • 参数在函数中,但每一个都是硬编码的:data-callback="doWork('Mike', 2)"
  • 是的,这就是为什么我的问题是,如何通过。我当然知道我们可以硬编码。 eval("function")(args) 不会工作
  • 好吧,我猜,如果 doWork 函数是全局的,您可以执行类似 window[ $(this).data("callback") ](arg1, arg2) 的操作,但这一切都太脏了:/ 通过 HTML 属性传递全局函数... yurgh...为什么不走干净的方式,只在隔离范围内编写 JS 函数?
【解决方案4】:

如果您真的想将函数(带或不带参数)从一件事传递到另一件事而不将它们作为事件绑定,您可以这样做(我从@Taplar 的回答开始)

<button type="submit" class="marker marker1"></button>
<button type="submit" class="marker marker2"></button>
<button type="submit" class="marker marker3"></button>
<button type="submit" class="marker marker4"></button>

<script>
  var $markers = $('.marker');
  $markers.filter('.marker1').get(0).customCallback =  doWork;
  $markers.filter('.marker2').get(0).customCallback = function(){ 
    doWork(arg0,arg1); 
  };
</script>

然后你可以在你的其他组件中访问它们:

<script>
  $('.marker').each(function(){
    var  thisFunction = $(this).get(0).customCallback;
    //do something useful with thisFunction
  });
</script>

【讨论】:

  • 这是一种有趣的方法,可以替代eval()。唯一的不足是与data-attributes 相比缺乏可读性,尤其是在使用其中的几个时。例如:data-title="Welcome" data-width="300" data-url="/.../..." data-callback="closeOtherDialogs"。无论如何,感谢您分享经验。
【解决方案5】:

你可以只绑定一个函数作为数据属性

const ele = $('button');

ele.data('onClick', evt => {
    alert('bye');
})

ele.click(evt => {
    const ele = $(evt.target);
    ele.data('onClick')(evt);
})

【讨论】:

  • 值得一提的是,这并不是将其作为数据属性存储在 DOM 中。 JQuery 将其抽象化并在其内部维护这些数据。只有字符串可以存储在属性中。这意味着它无法在原生 JS 中读取。仍然比使用 eval 更好的解决方案,假设 jQuery 是可用的。
【解决方案6】:

另一种方法是使用window[func](args)

类似于eval(),但您必须将函数名称和参数分别存储在 HTML 属性中。

这是一个简单的例子...... CodePen

<button type="submit" data-func="Angel" data-args='use me instead of evil()' class="marker">TEST</button>

<script type="text/javascript">

    //=== The Listener =====
    $(".marker").on("click",function(){

        // Get the function name and arguments
        let func = $(this).attr("data-func");
        let args = $(this).attr("data-args");

        // Call the function
        window[func](args);
    })


    //=== The Function =====
    function Angel(msg){
        alert(arguments.callee.name + " said : " + msg);
    }
</script>

【讨论】:

    猜你喜欢
    • 2014-03-24
    • 2018-12-04
    • 1970-01-01
    • 1970-01-01
    • 2012-12-20
    • 1970-01-01
    • 2018-05-13
    • 1970-01-01
    • 2015-12-25
    相关资源
    最近更新 更多