luke-chang / js-spatial-navigation

A javascript-based implementation of Spatial Navigation.

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

can navigate but not click on dynamic dom eliments

Rob2k9 opened this issue · comments

@luke-chang i am having a small issue when using this code i am pulling new eliments into dom using a json request and then making them focusable using SN.makeFocusable();

my issue is that when i click the enter key on the newly populated eliments it does not run the code to grab the id and run the appropriate code i have double checked this by doing the following

setting a default eliment clicking on it and javascript alert shows and then inner html is cleared and then the same default eliment is reappended and SN.makeFocusable(); is run again i can then navigate back to this default eliment but click on it does not run the function to show the alert even tho it worked when loaded with the page initially so any eliments loaded to dom after the initial page load and able to be navigated but cant be clicked

I'm curious how you bind the event handler to the newly added element. Could you provide some code snippet?

I'm curious how you bind the event handler to the newly added element. Could you provide some code snippet?

i am using the default code from example 3.1 i have just added new eliments with an id that runs a function the same as the settings modal and this then replaces the inner html of middle eliment with new eliments with specific ids but when i click on these elimtnes it dosent go through the ids and run the functions

` <script>
$(function() {

	$("#middlebox").fadeIn(3000);
	
    // A short name of the SpatialNavigation singleton object.
    var SN = SpatialNavigation;

    // Initialize
    SN.init();

    // Add the first section "upperbox".
    SN.add({
      id: 'upperbox',
      selector: '#upperbox .focusable',

      // Force to focus the "#button-settings" when entering this section.
      defaultElement: '#button-settings',
      enterTo: 'default-element'
    });

    // Add the second section "middlebox".
    SN.add({
      id: 'middlebox',
      selector: '#middlebox .focusable',

      // Focus the last focused element first then entering this section.
      enterTo: 'last-focused'
    });

    // Add the third section "settings-dialog".
    //
    // Any invisible elements can't be navigated at all so we can add this
    // section at anytime and don't need to concern their current status.
    SN.add({
      id: 'settings-dialog',
      selector: '#settings-dialog .focusable',

      // Since it's a standalone dialog, we restrict its navigation to
      // itself so the focus won't be moved to another section.
      restrict: 'self-only',

      // Note that we don't set "enterTo" to "default-element" in this
      // section because it's impossible to enter this section from the
      // others by arrow keys. This default element will only affect the
      // "focus('settings-dialog')" API call.
      defaultElement: '#button-cancel'
    });

    // Expand the "button-function" area.
    $('#button-function').on('sn:focused', function() {
		$('#button-function span').removeClass().addClass('fa fa-arrow-left');
		$('#button-function-area .sub-button').removeClass('hide');
    });

    // Collapse the "button-function" area.
    $('#button-function-area .button').on('sn:unfocused', function() {
      // Use "setTimeout" to defer the action so that we can get the next
      // focused element.
      setTimeout(function() {
        if (!$(':focus').is('#button-function-area .button')) {
			$('#button-function span').removeClass().addClass('fa fa-arrow-right');
			$('#button-function-area .sub-button').addClass('hide');
        }
      });
    });

    // Implement "ensureVisible" feature.
    $('#middlebox .focusable').on('sn:willfocus', function() {
      SN.pause();

      $(this).ensureVisible(function() {
        SN.focus(this);
        SN.resume();
      });

      return false;
    });

    $('.focusable')
      .on('sn:enter-down', function() {
        // Add "clicking" style.
        $(this).addClass('active');
      })
      .on('sn:enter-up', function() {
        var id = this.id;
        var $this = $(this);

        // Remove "clicking" style.
        $this.removeClass('active');

        // Do related actions according to the id of element.
        if (id.substr(0, 9) == 'settings-') {
          $this.find('i').toggleClass('fa-check');
        } else {
          switch(id) {
            case 'button-settings':
              // Show the settings dialog
              $('#settings-container').removeClass('hide');
			  $("#settings-dialog").fadeIn(3000);

              // Move focus to section "settings-dialog"
              SN.focus('settings-dialog');
              break;
            case 'button-save':
            case 'button-cancel':
              // Hide the settings dialog
              $('#settings-container').addClass('hide');

              // Move focus back to section "upperbox".
              SN.focus('upperbox');
              break;
            case 'show-alert':
              // Hide the settings dialog
			  var optiontext = $this.data("text")
			  Swal.fire({
			    position: 'center',
				backdrop: true,
				timerProgressBar: true,
				color: 'white',
				background: '#020D18',
			    title: optiontext,
			    showConfirmButton: false,
			    timer: 1500
			  })
              // Move focus back to section "upperbox".
              SN.focus('middlebox');
              break;
            case 'get-tv-categories':

				var API_URL = window.location.href + 'controller.php?op=category_series&username='+$("#username").val()+'&password='+$("#password").val();

				$.getJSON(API_URL).done(function(RESULT) {
					
					$("#middlebox").html("");
					$("#middlebox").css("display", "none");
					
					$.each(RESULT.items, function(i, item) {
						
						_ID      = item.category_id;
						_NAME    = item.category_name;
						
						
						$("#middlebox").append('<div class="genre focusable" id="shows" data-action="Shows" data-id="'+ _ID +'" ><p>'+ _NAME +'</p></div>');

					});// END OF EACH
					
					$("#middlebox").fadeIn(3000);
					
					SN.makeFocusable();
					SN.focus('middlebox');
					
				});// END OF GET JSON
				
				break;
            case 'get-movie-categories':

				var API_URL = window.location.href + 'controller.php?op=category_vods&username='+$("#username").val()+'&password='+$("#password").val();

				$.getJSON(API_URL).done(function(RESULT) {
					
					$("#middlebox").html("");
					$("#middlebox").css("display", "none");
					
					$.each(RESULT.items, function(i, item) {
						
						_ID      = item.category_id;
						_NAME    = item.category_name;
						
						
						$("#middlebox").append('<div class="genre focusable" id="show-alert" data-text="FireFox" ><p>'+ _NAME +'</p></div>');

					});// END OF EACH
					
					$("#middlebox").fadeIn(3000);
					
					SN.makeFocusable();
					SN.focus('middlebox');
					
				});// END OF GET JSON
				
				break;
            case 'get-movies-by-categories':
				
				var API_URL = window.location.href + 'controller.php?op=vods&category='+$this.data("text")+'&username='+$("#username").val()+'&password='+$("#password").val();

				$.getJSON(API_URL).done(function(RESULT) {
					
					$("#middlebox").html("");
					$("#middlebox").css("display", "none");
					
					$.each(RESULT.items, function(i, item) {
						
						_ID      = item.category_id;
						_NAME    = item.category_name;
						_POSTER    = item.stream_icon;
						
						
						$("#middlebox").append('<div class="poster focusable" id="shows" data-action="Play" data-id="'+ _ID +'" ><img src="'+_POSTER+'" alt="info" class="posterimg"></div>');

					});// END OF EACH
					
					$("#middlebox").fadeIn(3000);
					
					SN.makeFocusable();
					SN.focus('middlebox');
					
				});// END OF GET JSON
				
				break;
            case 'get-live-categories':

				var API_URL = window.location.href + 'controller.php?op=category_channels&username='+$("#username").val()+'&password='+$("#password").val();

				$.getJSON(API_URL).done(function(RESULT) {
					
					$("#middlebox").html("");
					$("#middlebox").css("display", "none");
					
					$.each(RESULT.items, function(i, item) {
						
						_ID      = item.category_id;
						_NAME    = item.category_name;
						
						
						$("#middlebox").append('<div class="genre focusable" id="shows" data-text="'+ _ID +'" ><p>'+ _NAME +'</p></div>');

					});// END OF EACH
					
					$("#middlebox").fadeIn(3000);
					
					SN.makeFocusable();
					SN.focus('middlebox');
					
				});// END OF GET JSON
				
				break;
            case 'Home':
					
				$("#middlebox").html("");
				$("#middlebox").css("display", "none");
					
				$("#middlebox").append('<div class="info focusable" id="show-alert" data-text="FireFox" ><img src="info.png" alt="info" class="responsive"></div>');

				$("#middlebox").fadeIn(3000);
				
				SpatialNavigation.init();
				
				SN.makeFocusable();
				SN.focus('middlebox');
				
				break;
			}
		}

        // For testing only.
        //console.log(id);
      });

    // Press ESC key to dismiss the settings dialog.
    $(window).keydown(function(evt) {
	  alert(evt.keyCode);
      if (evt.keyCode == 27 && !$('#settings-container').hasClass('hide')) {
        $('#settings-container').addClass('hide');
        SN.focus('upperbox');
        return false;
      }
    });

    // Set everything with "tabindex=-1".
    SN.makeFocusable();
	
    // Focus section "middlebox" by default.
    SN.focus('upperbox');
  });
</script>

`

You just don't bind the event handler to the newly-added elements, do you? $('.focusable').on() affects only existing elements.

You just don't bind the event handler to the newly-added elements, do you? $('.focusable').on() affects only existing elements.

ahh i never noticed that how would i go about fixing it so all newly added eliments can automatically be selected

if there any chance you would implament gamepad support in the future using the gapepad api ?

There are many ways to achieve it according to your purpose. Basically you can just manually bind another event handler right after your $("#middlebox").append() is invoked. It's nothing to do with this library though.

BTW, there isn't a plan for Gamepad API anytime soon.

There are many ways to achieve it according to your purpose. Basically you can just manually bind another event handler right after your $("#middlebox").append() is invoked. It's nothing to do with this library though.

BTW, there isn't a plan for Gamepad API anytime soon.

i have fixed the issue by changing the code to

$("#body").on("sn:enter-down", ".focusable", function(){ i dont know if you would edit your master to this that way dynamic eliments are supported out of the box

Supporting future elements isn't a part of the purpose of the demo.

In practice, I personally would not put everything in a single event handler. Just like I don't prefer to bind a single global click event handler to all the elements in the page. Therefore, there, in my opinion, is no need to support this case especially in such a simple demo page.

However, I'm glad that you find a way to achieve your goal. Let's close this issue accordingly. Thanks for your feedback.