backbone events not triggering in Safari

For a learning exercise I converted a sinatra/backbone app to the Rails environment. I got it working on Chrome and Firefox but it doesn't work on Safari. It turns out that the original app http://backbone-hangman.heroku.com doesn't work on Safari either. When you click "new game" it doesn't seem to fire an event. The Safari console doesn't show any errors (although I'm not that experienced with Safari's developer tools as I never use them).

Since there is a live version of the app available here http://backbone-hangman.heroku.com I won't post a lot of code, but this is the view code that sets an event on click #new_game, triggering the startNewGame function. Nothing happens in Safari. Source code for the original is here https://github.com/trivektor/Backbone-Hangman

I googled a bit and found some mention of Safari treating events differently but couldn't find a solution. Can any recommend anything?

$(function() {

  window.OptionsView = Backbone.View.extend({
    el: $("#options"),
    initialize: function() {
      this.model.bind("gameStartedEvent", this.removeGetAnswerButton, this);
      this.model.bind("guessCheckedEvent", this.showGetAnswerButton, this);
    },
    events: {
      'click #new_game': 'startNewGame',
      'click #show_answer': 'showAnswer'
    },
    startNewGame: function() {
      this.model.new();
    },
    removeGetAnswerButton: function() {
      $("#show_answer").remove();
    },
    showGetAnswerButton: function(response) {
      console.log("showGetAnswerButton");
      console.log(response);
      var threshold = this.model.get("threshold");
      console.log(threshold);
      if (response.incorrect_guesses == this.model.get("threshold")) {
        $(this.el).append('<input type="button" id="show_answer" class="action_button" value="Show answer" />');
      }
    },
    showAnswer: function() {
      this.model.get_answer();
    }
  })

})

Update

Based on one of the comments below the OP, I'm posting more code. This is hangman.js where the objects are instantiated

var game = new Game

  var options_view = new OptionsView({model: game});

  var characters_view = new CharactersView({model: game});

  var hint_view = new HintView({model: game});

  var word_view = new WordView({model: game});

  var hangman_view = new HangmanView({model: game});

  var answer_view = new AnswerView({model: game});
  var stage_view = new StageView({model: game});

The views and models are attached to the window like this

window.AnswerView = Backbone.View.extend({ ...

Update Aside from Backbone, jQuery and Underscore which are loaded sitewide, the following files are loaded for this specific app in the Rails system.

在这里输入图像描述


This is jQuery + Safari issue (document.ready)

You can just move your scripts inside the body tag and remove $(function(){ /**/ }) wrapper in every file.

Also I added requirejs support and made pull request

EDIT:

First of all sorry for my English :)


File views/index.haml:

We should embed js at the bottom of the page (to avoid Safari error)

= javascript_include_tag "javascript/require.js", :"data-main" => "javascript/config"

Here javascript/config is the path to requirejs config.


File public/javascript/config.js:

"deps" : ["hangman"]

This means that application will start with hangman.js


File public/javascript/hangman.js:

We don't need $(function() { wrapper because our script initialized from the body and document is already 'ready'

define([
  'models/game',
  'views/answerView',
  /* ... */
],
function(Game, OptionsView, /* ... */) {
  // ...
}

Here we load our modules (first array element will be available in the first function argument and so on)


Other files

We just replace $(function() { with define(['backbone'], function(Backbone) {

In the first line we load backbone module. When it will be fetched it will be available inside anonymous function (first parameter - Backbone)

Next we should return the view to avoid undefined module value ( public/javascript/hangman.js file should initialize a lot views. It can't initialize undefined it should initialize Backbone.View that we should return)


To learn more you should read requirejs documentation. I recomend you to start with this article


Try this instead.

var OptionsView = Backbone.View.extend({
    el: $("#options"),
    initialize: function() {
      this.model.bind("gameStartedEvent", this.removeGetAnswerButton, this);
      this.model.bind("guessCheckedEvent", this.showGetAnswerButton, this);
    },
    events: {
      'click #new_game': 'startNewGame',
      'click #show_answer': 'showAnswer'
    },
    startNewGame: function() {
      this.model.new();
    },
    removeGetAnswerButton: function() {
      $("#show_answer").remove();
    },
    showGetAnswerButton: function(response) {
      console.log("showGetAnswerButton");
      console.log(response);
      var threshold = this.model.get("threshold");
      console.log(threshold);
      if (response.incorrect_guesses == this.model.get("threshold")) {
        $(this.el).append('<input type="button" id="show_answer" class="action_button" value="Show answer" />');
      }
    },
    showAnswer: function() {
      this.model.get_answer();
    }
  });

Your code dosen't need to be in a document ready (it's not directly manipulating DOM, it's just declaring an object);

Make sure game.js goes after all your declarations though.

It looks like a problem Safari has in adding variables to the Global Object. Using var in the global context makes sure window.OptionsView exists. You might want to consider using require.js in the future to manage all of these global object problems.

链接地址: http://www.djcxy.com/p/65530.html

上一篇: 实施许多可扩展的方法

下一篇: 骨干事件不会在Safari中触发