Javascript webkit 上未调用对象回调

Javascript object callback not being called on webkit

我定义了一个 js "class" 这样的:

var TelephoneFormGroup = function(targetId, removePhoneCallback) {

    this.phone = '';
    this.phone_type_options = [];
    this.counter = 0;
    this.targetId = targetId;
    this.form_group = {};
    this.removePhone = removePhoneCallback;

    this.buildFormGroup = function() {
        var outerDiv = document.createElement('div');
        outerDiv.className = 'form-group';

        var label = document.createElement('label');
        label.className = 'label-control col-sm-3';

        var innerDiv = document.createElement('div');
        innerDiv.className = 'col-sm-8';

        var inputId = document.createElement('input');
        inputId.type = 'hidden';
        inputId.name = 'data[Telephone][' + this.counter + '][id]';

        var inputPhoneTypeId = document.createElement('select');
        inputPhoneTypeId.name = 'data[Telephone][' + this.counter + '][phone_type_id]';
        inputPhoneTypeId.className = 'form-control col-sm-3';

        var removePhoneIcon = document.createElement('i');
        removePhoneIcon.className = "glyphicon glyphicon-remove";

        var removePhoneLink = document.createElement('a');
        removePhoneLink.appendChild(removePhoneIcon);
        removePhoneLink.title = "Remover Telefone";
        removePhoneLink.href = "#";
        removePhoneLink.dataset.toggle = "tooltip";

        removePhoneLink.onclick = this.removePhone;

        label.appendChild(removePhoneLink);
        label.innerHTML += 'Telefone ' + this.counter;

        var that = this;
        Object.keys(this.phone_type_options).forEach( function(key) {
            inputPhoneTypeId.options[inputPhoneTypeId.options.length] = new Option(that.phone_type_options[key], key);
        });

        var inputPhone = document.createElement('input');
        inputPhone.type = 'text';
        inputPhone.name = 'data[Telephone][' + this.counter + '][phone]';
        inputPhone.className = 'form-control phone col-sm-9';


        outerDiv.appendChild(label);
        outerDiv.appendChild(innerDiv);
        innerDiv.appendChild(inputId);
        innerDiv.appendChild(inputPhoneTypeId);
        innerDiv.appendChild(inputPhone);

        this.form_group = outerDiv;
    },

    this.render = function() {
        if (this.targetId == null || this.targetId == '') throw 'Empty target id';

        if (typeof(this.targetId) === 'string') {
            document.getElementById(this.targetId).appendChild(this.form_group);    
        } else if (typeof(this.targetId === 'object')) {
            this.targetId.appendChild(this.form_group);
        };

    }

};

我的问题是 removePhoneLink.onclick = this.removePhone; 没有在 webkit 浏览器上触发。在 Firefox (mac) 上正常工作,但在 safari/chrome (mac)、safari/chrome (ipad).

上不工作

我错过了什么?

谢谢!

tl;dr 在附加事件处理程序之前将元素附加到 DOM

感谢您提供有趣的案例。几次尝试后,我认为 WebKit 在注册事件处理程序之前需要附加到 DOM 的元素(偶然发现 Todd Motto's example,他在附加事件处理程序之前确实获得了带有 jQuery 的元素! )

这是您的代码 a working fiddle,带有注释。 主要是,我在 render 方法之后添加了一个调用的 bindEventHandlers 方法,使整个对象成为一个带有链接方法的流畅接口(a la jQuery),在 "class" 上添加了一个静态计数器。 简短摘录:

  • 跟踪实例化了多少对象

    TelephoneFormGroup.counter = ++TelephoneFormGroup.counter || 0;
    this.counter = TelephoneFormGroup.counter;
    
  • 将回调封装在一个方法中并用 apply 调用它(确保在正确的对象上调用该方法,在另一个项目上有一些问题)

    this.removePhone = function(){
        removePhoneCallback.apply(this);
    };
    
  • this 绑定到您的 Object.keysforEach(删除了 var that=this; 技巧)

    Object.keys(this.phone_type_options).forEach( function(key) {
        inputPhoneTypeId.options[inputPhoneTypeId.options.length] = new Option(this.phone_type_options[key], key);
    }.bind(this));
    // Here, we tell forEach we want 'this' to be bound to the object, not the current item being iterated over (i.e. that=this trick)
    
  • 由于 removePhoneLink 构造现在分两个阶段构建(构建标记,然后附加事件处理程序),我们使用动态创建的 id

    将其耦合
    ...
    // in buildFormGroup()
    removePhoneLink.id = "btnRemove"+this.targetId;
    ...
    
    // called after attaching the form group to the dom
    this.bindEventHandlers = function() {
        document.getElementById("btnRemove"+this.targetId).onclick = function(e) {
            this.removePhone();
        }.bind(this);
    }
    

我希望它适用于您的用例。我在 Chrome 和 Safari (Mac OS X Yosemite).

上试过了

edit : 我发现您的代码有趣、简单,但对于我考虑了一段时间的小测试来说足够复杂了。您的所有方法都绑定到实例化对象。使用 JavaScript 和原型模式,您应该向对象的原型添加方法(如 this fiddle for your code)。
我做了一个小的基准测试来比较这两种技术,检查内存使用情况和完成方法的时间 ()。
我会使用 Chrome 开发人员工具(堆大小 运行s,在 运行s 之间刷新,然后渲染时间 运行s,在 运行s 之间刷新)。 查找 the results there (fiddle.md).
如您所见,在实例化 50 个对象之前,它不会产生太大差异。然后,您可以使用原型将渲染速度提高 18%,并且内存使用率提高近 30%(对于 50'000 个对象 运行)。 所以我想对于生产用途,去寻找原型?