简体   繁体   中英

Is it effective/efficient to use jQuery with OOP JS, and if so, how do I create a changing-state menu across class functions?

I'm trying to write a JavaScript widget in object-oriented JavaScript, or at least what I understand JS' near-equivalent of classes to be.

All I'm trying to achieve is a menu which changes state. In pseudo;

  • Upon widget load, populate #nav with a <ul> menu containing year group <li> s.
  • Upon clicking a year group <li> , empty #nav ul of its <li> and re-populate it with tutor group <li> s in that year, as well as appending a return link.
  • Upon clicking the return link, empty #nav and re-populate it with the original year group <ul> menu whilst being able to utilise all of the above functionality.

Historically , ie to date, I've used "embedded" functions to try and keep everything within the same DOM layer/timing. I don't really understand how any of this works, or if this is an appropriate thing to do, but the code itself seemed to work.

Here's an example of the way I've done it previously:

var GroupsDisplayHTML = '<h2>Tutor Groups</h2>' +
    '<ul><li><a class="year" href="javascript:void(0);" title="Year 9" id="9">Year 9</a></li>' +
    '<li><a class="year" href="javascript:void(0);" title="Year 10" id="10">Year 10</a></li>' +
    '<li><a class="year" href="javascript:void(0);" title="Year 11" id="11">Year 11</a></li>' +
    '<li><a class="year" href="javascript:void(0);" title="Year 12" id="12">Year 12</a></li>' +
    '<li><a class="year" href="javascript:void(0);" title="Year 13" id="13">Year 13</a></li></ul>';

$("div#groups").html(GroupsDisplayHTML);

$('a.year').click( function() {
    var Groups;
    var Groups_Sorted = [];

    Frog.API.get('groups.getAll',
    {
        'onSuccess': function (data) { Groups = data; },
        'onError': function(err) { alert(err); }
    });

    for (var i = 0; i < Groups.length; i++) {
        var Year = $(this).attr("id");

        if (Groups[i].name.indexOf(Year) == 0 && Groups[i].name.indexOf('/Tp') != -1) {
            var arrayToPush = { 'id': Groups[i].id, 'name': Groups[i].name };
            Groups_Sorted.push(arrayToPush);
        }
    }

    GroupsDisplayHTML = '<h2>Year ' + Year + '</h2><ul>';

    for(var i = 0; i < Groups_Sorted.length; i++){
        GroupsDisplayHTML += '<li><a class="group" href="javascript:void(0);" title="' + Groups_Sorted[i].id + '" id="' + Groups_Sorted[i].id + '">' + 
            Groups_Sorted[i].name + ' <span style="font-size:10px;color:#bbb;">(' + Groups_Sorted[i].name + ')</span></a></li>';
    }

    GroupsDisplayHTML += '<li><a class="return" href="javascript:void(0);">&lt;- Back to Year Groups</a></li></ul>';

    $("div#groups").html(GroupsDisplayHTML);

    $('a.group').click( function() {
        var Group_ID = $(this).attr("id");
        AssignPoints.getUsers(Group_ID);
    });

    $('a.return').click( function() {
        AssignPoints.displayGroups(data);
    });
});

However, now, I'm wondering if the better way to do it is to use jQuery's on function. Here's the code I'm currently writing (*just to try and achieve the changing-state menu):

var TutorGroupPoints = {
    URL: 'http://staff.curriculum.local/frog/rewards.php',
    currentUser: UWA.Environment.user.id,
    groupsObject: { },
    sortedArray: [ ],
    navHTML: '<h2>Tutor Groups</h2>' +
        '<ul>' +
            '<li><a class="year" title="Year 9" id="9">Year 9</a></li>' +
            '<li><a class="year" title="Year 10" id="10">Year 10</a></li>' +
            '<li><a class="year" title="Year 11" id="11">Year 11</a></li>' +
            '<li><a class="year" title="Year 12" id="12">Year 12</a></li>' +
            '<li><a class="year" title="Year 13" id="13">Year 13</a></li>' +
        '</ul>',

    init: function() {
        /* caching outer this -> AJAX scope problems */
        var that = this;

        /* retrieve all of the user groups from Frog VLE and store them in an object-level variable */
        Frog.API.get('groups.getAll',
        {
            'onSuccess': function (data) { that.groupsObject = data; },
            'onError': function(err) { alert(err); }
        });

        /* populate the div#nav with our year group UL */
        $('#nav').append(this.navHTML);

        /* using "on" because the LIs have been created in the DOM? */
        $('#nav ul').on("click", "li a", function(e) {
            e.preventDefault();
            that.yearClick( $(this).attr("id") );
        });
    },

    yearClick: function(year) {
        /* run through groupsObject and pull out any tutor groups found in the year we've clicked on
           then put those into our sortedArray */
        for (var i = 0; i < this.groupsObject.length; i++) {
            if (this.groupsObject[i].name.indexOf(year) == 0 && this.groupsObject[i].name.indexOf('/Tp') != -1) {
                /* all we need is name and id */
                var arrayToPush = { 'id': this.groupsObject[i].id, 'name': this.groupsObject[i].name };
                this.sortedArray.push(arrayToPush);
            }
        }

        /* clear the existing UL from div#nav */
        $('#nav ul').empty();

        /* populate div#nav's UL with LIs of our tutor groups (label being name, attr="id" being id) and
           clickable links for each */
        for (var i = 0; i < this.sortedArray.length; i++) {
            $('#nav ul').append('<li><a class="tutor" id="' + this.sortedArray[i].id + '">' + this.sortedArray[i].name + '</a></li>');
        }

        /* add a "return" link to view other years' tutor groups */
        $('#nav ul').append('<li><a id="return">&lt;- Return</a></li>');

        /* upon clicking the return link, empty #nav ul then re-append our navHTML from earlier */
        $('#nav ul').on("click", "a#return", function(e) {
            e.preventDefault();
            $('#nav ul').empty();
            $('#nav').append(this.navHTML);
        });

        /* upon clicking any of our tutor group LIs, display that link's id so that we can use it in
           another function to actually display some content */
        $('#nav ul').on("click", "a.tutor", function(e) {
            e.preventDefault();
            alert( $(this).attr("id") );
        });
    }

};

widget.onLoad = function(){
    /* run our "class" */
    TutorGroupPoints.init();
}

And the HTML:

<div id="wrapper">
    <div id="nav">

    </div>
    <div id="content">

    </div>
</div>

I've created a jsFiddle here , which is a slight manipulation of the code (ie I've removed the inaccessible Frog API calls and replaced them with a fixed object). Hopefully this works adequately.

The problem here is that once I click the return button, nothing happens . In addition, I suspect that fixing the return button would then provide me with some links which, when clicked upon, would do nothing.

Ideally , as per the bottom end of the code, I would like to run another function within my TutorGroupPoints class when the user clicks on a specific tutor group. How will this affect my return button, etc? Will they then stop working because they aren't being run from the "current" function?

More than "just" an answer to my code problems, I'd quite like some direction , ie is the recent code I've written better than the older stuff? Should I be using on or should I be using embedded jQuery functions and communicative class functions?

Thanks in advance,

Short answer: http://jsfiddle.net/byzgv/4/

I'm not sure about the OOP aspect, but this seems to be your scenario:

  1. when the page is loaded, you insert a list into a div.
  2. you add some event handlers to this list
  3. you click a list item, your event handlers run, and change the list items.
  4. the "Return" list item "does nothing" when you click it.

In your fiddle, clicking the "Return" list item does do something, it empties this list. This is partly what you asked it to do:

$('#nav').on("click", "ul a#return", function(e) {
    e.preventDefault();
    $('#nav ul').empty();
    $('#nav').append(this.navHTML);
});

So this event handler has fired, and the first two instructions have worked. The call to append doesn't work because you're in a different context - this does not refer to the TutorGroupPoints object. So you can fix this by referring to your object explicitly:

$('#nav').on("click", "ul a#return", function(e) {
    e.preventDefault();
    $('#nav ul').empty();
    $('#nav').append(TutorGroupPoints.navHTML);
});

(by the way, you're appending a whole new list, so you need to do $('#nav').empty(); instead of $('#nav ul').empty(); )

Then, as you guessed, clicking the "Return" item gives you a new list which doesn't respond to click events. This is because of how you're calling the on function. The jQuery object you call on on must exist when the event handler is being set up. In your init function, the list does exist when you set up the event handlers, as you're appending it to the div before calling on . But later, you trash this list and replace it.

To get your event handlers working on all lists rather than just the first one, you need to attach your event handlers to an element that exists from the start and doesn't change, ie the #nav div. So

$('#nav ul').on("click", "li a", function(e) {

can become

$('#nav').on("click", "ul li a", function(e) {

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM