2013-04-02 232 views
2

我有這些div叫做節拍,我想單​​擊它們時進行註冊。但是,我似乎無法讓他們註冊點擊,無論是通過點擊它們,還是在控制檯中的特定div上調用JQuery單擊事件。無論哪種方式,沒有什麼是註冊。不綁定到DOM元素的骨幹點擊事件綁定

measureView.js創建此beatView,它在父度量內創建節拍。

beatView.js:

//filename: views/beats/beatView.js 
/* This is the view for a single beat, which is contained in a measure view. */ 
define([ 'jquery', 'underscore',  'backbone',  'backbone/models/beat',  'text!backbone/templates/measures/audioMeasures.html',  'text!backbone/templates/beats/linearBarBeats.html',  'text!backbone/templates/beats/linearBarSVGBeats.html',  'text!backbone/templates/beats/circularPieBeats.html',  'app/dispatch',  'app/log' 
], function($, _, Backbone, BeatModel, audioMeasuresTemplate, linearBarBeatsTemplate, linearBarSVGBeatsTemplate, circularPieBeatsTemplate, dispatch, log){ 
    return Backbone.View.extend({ 
    //registering backbone's click event to our toggle() function. 
    events : { 
     'click' : 'toggle' 
    }, 

    //The constructor takes options because these views are created by measuresView objects. 
    initialize: function(options){ 
     if (options) { 
     console.log('options :'); 
     console.warn(options); 
     this.model = options.model; 
     // this.parentEl should be measure.cid 
     this.measureBeatHolder = options.parentElHolder; 
     } else { 
     console.log('never really getting here'); 
     this.model = new BeatModel; 
     }  
     this.render(); 
    }, 

    //We use css classes to control the color of the beat. A beat is essentially an empty div. 
    render: function(toggle){ 
     var state = this.getSelectionBooleanCSS(); 
     if (toggle) { 
     $('#beat'+toggle).removeClass(state); 
     $('#beat'+toggle).addClass(this.switchSelectionBooleanValue()); 
     } else { 
     var compiledTemplate = _.template(this.representations[this.currentBeatRepresentation], {beat: this.model, beatAngle: this.beatAngle, state: state}); 
     $(this.measureBeatHolder).append(compiledTemplate); 
     return this; 
     } 
    }, 

    getSelectionBooleanCSS: function(){ 
     if (this.model.get("selected")) { 
     return "ON"; 
     } else { 
     return "OFF"; 
     } 
    }, 

    switchSelectionBooleanValue: function(){ 
     if (this.model.get('selected') == true) { 
     this.model.set('selected', "false"); 
     } else { 
     this.model.set('selected', "true"); 
     } 
     return this.model.get('selected'); 
    }, 

    /* 
     This is called when a beat is clicked. 
     It does a number of things: 
     1. toggles the model's selected field. 
     2. re-renders the beat. 
     3. prints a console message. 
     4. tells log to send a log of the click event. 
     5. triggers a beatClicked event. 
    */ 

    toggle: function(){ 
     console.log('getting to toggle function'); 
     var selectedBool = this.model.get("selected"); 
     this.model.set("selected", !selectedBool); 
     var newBool = this.model.get("selected"); 
     this.render(this.model.cid); 
     dispatch.trigger('beatClicked.event'); 
    } 
    }); 
}); 

對於參考:

beatModel:

//filename: models/beat.js 
/* 
    This is the beat model. 
    It only knows about whether or not it 
    is selected. 
*/ 
define([ 
    'underscore', 
    'backbone' 
], function(_, Backbone) { 
    var beatModel = Backbone.Model.extend({ 
    defaults: { 
     selected: false, 
     state: 'OFF' 
    }, 
    initialize: function(){ 
    }, 
    getStyleClass: function() { 
     if (this.selected) { 
     return 'ON'; 
     } 
     else { 
     return 'OFF'; 
     } 
    } 
    }); 

    return beatModel; 
}); 

measureModel:

//filename: models/measure.js 
/* 
    This is the measure model. 
    A component has a collection of these models. 
    these models have a collection of beats. 
*/ 
define([ 
    'underscore', 
    'backbone', 
    'backbone/collections/beats' 
], function(_, Backbone, beatsCollection) { 
    var measureModel = Backbone.Model.extend({ 
    defaults: { 
     label: '0/4', 
     beats: beatsCollection, 
     numberOfBeats: 0, 
     divisions: 8 
    }, 
    initialize: function(){  
    } 

    }); 

    return measureModel; 
}); 

measureView.js:

// Filename: views/measures/measuresView.js 
/* 
    This is the MeasuresView. 

    This is contained in a ComponentsView. 
*/ 
define([ 
    'jquery', 
    'underscore', 
    'backbone', 
    'backbone/collections/measures', 
    'backbone/collections/beats', 
    'backbone/models/measure', 
    'backbone/views/beats/beatView', 
    'text!backbone/templates/measures/audioMeasures.html', 
    'text!backbone/templates/measures/linearBarMeasures.html', 
    'text!backbone/templates/measures/linearBarSVGMeasures.html', 
    'text!backbone/templates/measures/circularPieMeasures.html', 
    'app/dispatch', 
    'app/state', 
    'app/log' 
], function($, _, Backbone, MeasureModel, BeatsCollection, MeasuresCollection, beatView, audioMeasuresTemplate, linearBarMeasuresTemplate, linearBarSVGMeasuresTemplate, circularPieMeasuresTemplate, dispatch, state, log){ 
    return Backbone.View.extend({ 
    // el: $('.component'), 

    // The different representations 
    representations: { 
     "audio": audioMeasuresTemplate, 
     "linear-bar": linearBarMeasuresTemplate, 
     "linear-bar-svg": linearBarSVGMeasuresTemplate, 
     "circular-pie": circularPieMeasuresTemplate 
    }, 

    currentMeasureRepresentation: 'linear-bar', 

    //registering click events to add and remove measures. 
    events : { 
     'click .addMeasure' : 'add', 
     'click .delete' : 'remove' 
    }, 

    initialize: function(options){ 
     //if we're being created by a componentView, we are 
     //passed in options. Otherwise we create a single 
     //measure and add it to our collection. 
     if (options) { 
     this.measuresCollection = options.collection; 
     this.parent = options.parent; 
     this.el = options.el; 
     } 
     // else { 
     // this.measure = new BeatsCollection; 

     // for (var i = 0; i < 4; i++) { 
     //  this.measure.add(); 
     // } 

     // this.measuresCollection = new MeasuresCollection; 
     // this.measuresCollection.add({beats: this.measure}); 
     // } 

     if (options["template-key"]) { 
     this.currentBeatRepresentation = options["template-key"]; 
     } 

     //registering a callback for signatureChange events. 
     dispatch.on('signatureChange.event', this.reconfigure, this); 
     //Dispatch listeners 
     dispatch.on('measureRepresentation.event', this.changeMeasureRepresentation, this); 

     this.render(); 

     //Determines the intial beat width based on the global signature. Has to be below this.render() 
     this.calcBeatWidth(this.parent.get('signature')); 
    }, 

    changeMeasureRepresentation: function(representation) { 
     this.currentMeasureRepresentation = representation; 
     this.render();  
    }, 

    render: function(){ 
     $(this.el).html('<div class="addMeasure">+</div>'); 
     var measureCount = 1; 
     //we create a BeatsView for each measure. 
     _.each(this.measuresCollection.models, function(measure) { 
     // when representation button changes, the current representation template will get updated 
     var compiledTemplate = _.template(this.representations[this.currentMeasureRepresentation], {measure: measure, beatHolder:"beatHolder"+measure.cid, measureCount:measureCount, measureAngle: 360.0 }); 
     $(this.el).find('.addMeasure').before(compiledTemplate); 
      console.log('measure beats: '); 
      console.warn(measure.get('beats').models); 
      _.each(measure.get('beats').models, function(beat) { 
       // console.warn("#beat"+beat.cid.toString()); 
       new beatView({model:beat, parentElHolder:'#beatHolder'+measure.cid, parentCID:measure.cid, singleBeat:"#beat"+beat.cid}); 
      }, this); 
     measureCount ++; 
     }, this); 
     return this; 
    }, 

    /* 
     This is called when the user clicks on the plus to add a new measure. 

     It creates a new measure and adds it to the component. 
     It generates a string representing the id of the measure and the ids of 
     its beats and logs the creation. 

     Lastly, it triggers a stopRequest, because we can't continue playing until 
     all the durations get recalculated to reflect this new measure. 
    */ 
    add: function(){ 
     console.log('add measure'); 
     var newMeasure = new BeatsCollection; 

     for (var i = 0; i < this.parent.get('signature'); i++) { 
      newMeasure.add(); 
     } 

     this.measuresCollection.add({beats: newMeasure}); 

     //Logging 
     name = 'measure' + _.last(this.measuresCollection.models).cid + '.'; 
     _.each(newMeasure.models, function(beats) { 
      name = name + 'beat'+ beats.cid + '.'; 
     }, this); 
     log.sendLog([[3, "Added a measure: "+name]]); 

     //Render 
     this.render(); 
     //Dispatch 
     dispatch.trigger('stopRequest.event', 'off'); 
    }, 

    /* 
     This is called when the user clicks on the minus to remove a measure. 
    */ 
    remove: function(ev){ 
     if ($('#measure'+this.measuresCollection.models[0].cid).parent()) { 
     //removing the last measure isn't allowed. 
     if(this.measuresCollection.models.length == 1) { 
      console.log('Can\'t remove the last measure!'); 
      return; 
     } 
     console.log('remove measure'); 

     //we remove the measure and get its model. 
     var model = this.measuresCollection.get($(ev.target).parents('.measure').attr('id').replace('measure','')); 
     this.measuresCollection.remove(model); 

     //send a log event showing the removal. 
     log.sendLog([[3, "Removed a measure: measure"+model.cid]]); 

     //re-render the view. 
     this.render(); 

     //trigger a stop request to stop playback. 
     dispatch.trigger('stopRequest.event', 'off'); 
     dispatch.trigger('signatureChange.event', this.parent.get('signature')); 
     } 
    }, 
    // This is triggered by signatureChange events. 
    reconfigure: function(signature) { 
     console.log('MeasureView.reconfigure(signature) : signature=' +signature); 
     /* if the containing component is selected, this 
     triggers a request event to stop the sound. 

     Then this destroys the beat collection and creates 
     a new collection with the number of beats specified 
     by the signature parameter. 
     */ 
     if ($(this.parent).hasClass('selected')) { 
     dispatch.trigger('stopRequest.event', 'off'); 
     this.measure.reset(); 

     for (var i = 0; i < signature; i++) { 
      this.measure.add(); 
     } 
     //re-render the view. 
     this.render(); 

     //recalculate the widths for each beat. 
     this.calcBeatWidth(signature); 
     dispatch.trigger('signatureChange.event', this.parent.get('signature')); 

     } 
    }, 

    //This determines the width of each beat based on the 
    //number of beats per measure or 'signature'. 
    calcBeatWidth: function(signature) { 
     if ($(this.el).hasClass('selected')) { 
     var px = 100/$('.measure').css('width').replace(/[^-\d\.]/g, ''); 
     var beatWidth = (100 - ((signature*1+1)*px))/signature; 

     $(this.el).children('.beat').css({ 
      'width' : beatWidth+'%' 
     }); 
     } 
    } 
    }); 
}); 
+0

看來你的視圖元素永遠不會插入到DOM中。它是否被插入此代碼之外? – Loamhoof

回答

0

感謝@AlexMcp和@PaulHoenecke的貢獻。

我結束了通過JQuery的代理功能傳遞上下文給自己在渲染

// add click handler to this beat 
    $("#beat"+this.model.cid).click($.proxy(this.toggle, this)); 

完全beatView.js文件:

//filename: views/beats/beatView.js 
/* 
    This is the view for a single beat, which 
    is contained in a measure view. 
*/ 
define([ 
    'jquery', 
    'underscore', 
    'backbone', 
    'backbone/models/beat', 
    'text!backbone/templates/measures/audioMeasures.html', 
    'text!backbone/templates/beats/linearBarBeats.html', 
    'text!backbone/templates/beats/linearBarSVGBeats.html', 
    'text!backbone/templates/beats/circularPieBeats.html', 
    'app/dispatch', 
    'app/log' 
], function($, _, Backbone, BeatModel, audioMeasuresTemplate, linearBarBeatsTemplate, linearBarSVGBeatsTemplate, circularPieBeatsTemplate, dispatch, log){ 
    return Backbone.View.extend({ 

    /* TODO still issues with this 
     el: '.beat', 
     registering backbone's click event to our toggle() function. 
     events : { 
     'click' : 'toggle' 
     }, 
    */ 

    // The different representations 
    representations: { 
     "audio": audioMeasuresTemplate, 
     "linear-bar": linearBarBeatsTemplate, 
     "linear-bar-svg": linearBarSVGBeatsTemplate, 
     "circular-pie": circularPieBeatsTemplate 
    }, 
    currentBeatRepresentation: 'linear-bar', 
    beatAngle: 90, 

    //The constructor takes options because these views are created 
    //by measuresView objects. 
    initialize: function(options){ 
     if (options) { 
     // TODO: need to take in an option about currentBeatRep 
     // TODO: maybe need to respond to a representation changed event (change this.currentBeatRepresentation and rerender) 

     console.log('options :'); 
     console.warn(options); 
     this.model = options.model; 

     // this is the html element into which this class should render its template 
     this.measureBeatHolder = options.parentElHolder; 
     this.el = options.singleBeat; 
     this.parent = options.parent; 
     } else { 
     console.error('should not be in here!'); 
     this.model = new BeatModel; 
     } 
     this.render(); 
    }, 

    //We use css classes to control the color of the beat. 
    //A beat is essentially an empty div. 
    render: function(toggle){ 
     // the current state of the beat (is it ON or OFF?) 
     var state = this.getSelectionBooleanCSS(); 

     // if render is being called from the toggle function, we may want to do something different 
     if (toggle) { 
     $('#beat'+toggle).toggleClass("ON"); 
     $('#beat'+toggle).toggleClass("OFF"); 
     } else { 
     // this is reached during the initial rendering of the page 

     // compile the template for this beat (respect the current representation) 
     var compiledTemplate = _.template(this.representations[this.currentBeatRepresentation], {beat: this.model, beatAngle: this.beatAngle, state: state}); 
     // append the compiled template to the measureBeatHolder 
     $(this.measureBeatHolder).append(compiledTemplate); 
     // add click handler to this beat 
     $("#beat"+this.model.cid).click($.proxy(this.toggle, this)); 
     // $(this.parentEl).append(compiledTemplate); 
     return this; 
     } 
    }, 

    getSelectionBooleanCSS: function(){ 
     if (this.model.get("selected")) { 
     return "ON"; 
     } else { 
     return "OFF"; 
     } 
    }, 

    /* 
     This is called when a beat is clicked. 
     It does a number of things: 
     1. toggles the model's selected field. 
     2. re-renders the beat. 
     3. prints a console message. 
     4. tells log to send a log of the click event. 
     5. triggers a beatClicked event. 
    */ 
    toggle: function(){ 
     //switch the selected boolean value on the model 
     this.model.set('selected', !this.model.get('selected')); 
     //re-render it, passing the clicked beat to render() 
     this.render(this.model.cid); 
     // log.sendLog([[1, "beat" + this.model.cid + " toggled: "+!bool]]); 
     dispatch.trigger('beatClicked.event'); 
    } 
    }); 
}); 
3

骨幹需要您的events對象,並將所有這些事件類型和選擇器委託給視圖的el。您希望事件註冊的任何HTML都需要插入到視圖的el內,並且需要將el插入到頁面中。

通常設置我的看法是這樣的:

var myView = Backbone.View.extend({ 

    id: 'myView', 

    events: { 
    'click li' : 'myEventCallback' 
    }, 

    initialize: function() { 

    $('body').append(this.el); //el on the page now 
    this.render(); //fills up el with useful markup 
    }, 

    render: function() { 
    //fills el with useful markup from a template 
    this.el.html(JST['myTemplate' ]()); 
    }, 

    myEventCallback: function() { 
    //code for handling click events on the li's inside the el of this view 
    } 
}); 
+0

我添加了'el:'.beat''作爲臨時持有者,並且事件處理程序正常工作,然後在初始化(選項)中,我重置了el:this.el = options.singleBeat; '。我已經驗證了'options.singleBeat'正在爲節拍傳遞正確的'id',但是當我點擊任何一個節拍時,它會在所有後續節拍上調用切換功能,但不是它本身。所以如果愛有30個節拍,並且我點擊第一個節拍,剩下的29個會被切換,並且如果我點擊節拍29,最後一個會被切換,並且如果我點擊最後一個節拍,則什麼都不會發生。這似乎與數組相關..... –

+3

這將通過將類選擇器作爲el來混淆一些事情;對於相同的觀點,你會因爲太多的el而結束。你所描述的聽起來像你可能需要查看每個節拍。用你現在寫的東西,最快的成功路徑是將el選擇器改爲包含所有li.beats的父元素,而不要改變el。 view.delegate事件是附加事件監聽器的事件,並且只發生在初始化之前,所以更改視圖的el會導致不可預知的行爲 –

0

其實...所有你需要做的是在視圖的初始化函數中定義事件。