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;
#nav
with a <ul>
menu containing year group <li>
s. <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. 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);"><- 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"><- 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:
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.