SitePoint Sponsor

User Tag List

Page 2 of 3 FirstFirst 123 LastLast
Results 26 to 50 of 54
  1. #26
    Non-Member
    Join Date
    Feb 2012
    Posts
    892
    Mentioned
    10 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by Pullo View Post
    I'm always surprised that you can post so much from work.
    You must have an understanding boss.
    Or I must be doing my job well, which keeps my boss off my back

    Quote Originally Posted by donboe View Post
    So what is the problem if it is working?
    Actually, this thread displays two approaches that probably depend, as far as I can tell, on the way one enters JavaScript world, and webdev world for that matter.

    Pullo's approach is of a programmer that treats JavaScript as something which is HTML centric. It took JavaScript on because it had to, but it doesn't see JavaScript as a programming language, outside HTML and DOM.

    I give JavaScript more of a "classic" programming language approach, which only intersects HTML and DOM from time to time, when it absolutely needs to.

    Pullo, I'm sorry for making assumptions like this. Please correct me if I'm wrong.

  2. #27
    Gre aus'm Pott gold trophysilver trophybronze trophy
    Pullo's Avatar
    Join Date
    Jun 2007
    Location
    Germany
    Posts
    5,938
    Mentioned
    214 Post(s)
    Tagged
    12 Thread(s)
    Quote Originally Posted by donboe
    So what is the problem if it is working?
    No problem, just an opportunity
    The solution I gave you will work well and is relatively robust.
    But if you follow this thread, the chances are you will end up with something even better.

    Quote Originally Posted by myty View Post
    Actually, this thread displays two approaches that probably depend, as far as I can tell, on the way one enters JavaScript world, and webdev world for that matter.

    Pullo's approach is of a programmer that treats JavaScript as something which is HTML centric. It took JavaScript on because it had to, but it doesn't see JavaScript as a programming language, outside HTML and DOM.

    I give JavaScript more of a "classic" programming language approach, which only intersects HTML and DOM from time to time, when it absolutely needs to.

    Pullo, I'm sorry for making assumptions like this. Please correct me if I'm wrong.
    No, that's quite correct.
    I got into web design first and programming second (and unfortunately I studied neither )
    I'm very aware of the increasing omnipotence of JavaScript and I'm eager to learn more about the "classic" programming language approach.

  3. #28
    Gre aus'm Pott gold trophysilver trophybronze trophy
    Pullo's Avatar
    Join Date
    Jun 2007
    Location
    Germany
    Posts
    5,938
    Mentioned
    214 Post(s)
    Tagged
    12 Thread(s)
    Hi myty,

    Quote Originally Posted by myty View Post
    In the previous post I created an instance of MyDate class: date ...
    I've read through your post and I've understood everything this far.
    It's interesting to see that path this is taking and that you prefer to initialize each widget separately.
    Looking forward to the next part

  4. #29
    Non-Member
    Join Date
    Feb 2012
    Posts
    892
    Mentioned
    10 Post(s)
    Tagged
    0 Thread(s)
    Hey Pullo.

    Just for fun, run this in your console, where you opened my last example:
    Code:
    console.dir(window.document.childNodes[1].childNodes[2].childNodes[1].childNodes[3])
    It displays the input native "widget" object. Inspect it using the tree arrows.

    Looking at what is created based on the markup, I consider this to be the normal approach. Each widget should be an object, like it naturally is in JavaScript.

    This is the prototype chain for the native input "widget", the one you get by writing <input...>:

    HTMLInputElement -> HTMLElement -> Element -> Node -> EventTarget -> Object.

    Looking at the tree, you'll see that every input object/HTML element has it's own set of properties, like className, defaultValue, outerHTML, style, etcetera, but it inherits functionality from up the prototype chain. It may be a scary looking tree, but that's the gist of it.

    Following the same pattern, MyDate widget will fit right in. And this is what I'm doing.

  5. #30
    Gre aus'm Pott gold trophysilver trophybronze trophy
    Pullo's Avatar
    Join Date
    Jun 2007
    Location
    Germany
    Posts
    5,938
    Mentioned
    214 Post(s)
    Tagged
    12 Thread(s)
    I think you nailed it when you said that I treat JavaScript as something which is HTML-centric - (which I do / have done).

    I'm familiar with the concepts of classes, inheritance, polymorphism etc from the world of Ruby.

    E.g.

    Code:
    class Parent
      def parent_method
        puts "Parent method"
      end
    end
    
    class Child < Parent
      def child_method
        puts "Child method"
      end
    end
    
    p = Parent.new
    p.parent_method
    => Parent method
    
    c = Child.new
    c.child_method
    c.parent_method
    => "Child method"
    => "Parent method"
    
    p Parent.ancestors
    p Child.ancestors
    
    => [Parent, Object, Kernel, BasicObject]
    => [Child, Parent, Object, Kernel, BasicObject]
    But I hadn't really thought about applying this to JS, as my day to day work doesn't really require me to do more than manipulate the DOM.

  6. #31
    Non-Member
    Join Date
    Feb 2012
    Posts
    892
    Mentioned
    10 Post(s)
    Tagged
    0 Thread(s)
    Next step is adding date helper elements. The target input text element will be hidden and three new select elements will help the user choose the date.

    I've added a HELPER_ELEMENTS_HTML_FRAGMENT constant and a addHelperElements method. Now I can create an adjacent fieldset after the target element, with the helper select elements for day, month and year.


    date.html
    Code:
    <!doctype html>
      <html>
      <head>
        <meta charset="utf-8">
        <title>Date widget</title>
      </head>
    
      <body>
        <div class="dateDropdown">
          <label for="startDate">Please enter the start date:</label>
          <input id="startDate" type="text" placeholder="dd.mm.yyyy"/>
        </div>
    
        <div class="dateDropdown">
          <label for="endDate">Please enter the end date:</label>
          <input id="endDate" type="text" placeholder="dd.mm.yyyy"/>
        </div>
        
        <script src="date.js"></script>
        <script src="demo.js"></script>
      </body>
    </html>
    date.js
    Code:
    function MyDate(id) {
        this.targetElement = document.getElementById(id);
    };
    
    MyDate.prototype = {
      
        MONTHS: ['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sept','Oct','Nov','Dec'],
      
        YEAR_OFFSET: 21,
    
        HELPER_ELEMENTS_HTML_FRAGMENT: '<fieldset class="mydate"><select class="day"></select><select class="month"></select><select class="year"></select></fieldset>',
      
        getToday: function () {
            var today = new Date();
          
            return {
                day: today.getUTCDate(),
                month: today.getUTCMonth(),
                year: today.getUTCFullYear()
            }; 
        },
      
        getDaysInMonth: function (month, year) {
            return new Date(year, month, 0).getDate();
        },
        
        toggle: function () {
            var elemStyle = this.targetElement.style;
            
            if (elemStyle.display === "none") {
                elemStyle.display = "";
            } else {
                elemStyle.display = "none";
            };
        },
    
        addHelperElements: function() {
              this.targetElement.insertAdjacentHTML('afterend', this.HELPER_ELEMENTS_HTML_FRAGMENT);
        }
    };
    demo.js
    Code:
    var sdat = new MyDate("startDate"),
        edat = new MyDate("endDate");
    
    sdat.toggle();
    sdat.addHelperElements();

  7. #32
    Gre aus'm Pott gold trophysilver trophybronze trophy
    Pullo's Avatar
    Join Date
    Jun 2007
    Location
    Germany
    Posts
    5,938
    Mentioned
    214 Post(s)
    Tagged
    12 Thread(s)
    Ok, neat!

    I'm always wary of creating strings of HTML and then inserting them in the DOM having heard that this is "bad practice".
    Is there any reason that you are doing this, as opposed to creating a fieldset and three select elements (using document.createElement) and inserting them?

    I do actually prefer your approach as it is considerably less verbose.

  8. #33
    Non-Member
    Join Date
    Feb 2012
    Posts
    892
    Mentioned
    10 Post(s)
    Tagged
    0 Thread(s)
    It depends on how you insert them in the DOM, rather than what it is that you add to the DOM: one by one or all in one go.

    DOM insert or delete operations are slow. The practice being painted as "bad practice" is the one when you add one element at one time in the DOM.

    What you should do is create an HTML fragment, containing all the elements. After that, you should add the whole node to the DOM in one go.

    In short, a good practice is a one time DOM modification with all the elements, not several DOM modifications for each element.

    As the method says, it's for HTML fragments. It means it will first parse the whole HTML, and then add all four elements: one fieldset and three select elements, in one go. The bad practice here would be to call createElement four times.

    There are many ways to first create a HTML fragment and then add it to the DOM. This is just one of them. But the principle stands: if you're going to touch the DOM, do it rarely and make sure it's worth it.

  9. #34
    Gre aus'm Pott gold trophysilver trophybronze trophy
    Pullo's Avatar
    Join Date
    Jun 2007
    Location
    Germany
    Posts
    5,938
    Mentioned
    214 Post(s)
    Tagged
    12 Thread(s)
    Quote Originally Posted by myty View Post
    But the principle stands: if you're going to touch the DOM, do it rarely and make sure it's worth it.
    That's a good rule of thumb. Thanks.

  10. #35
    Non-Member
    Join Date
    Feb 2012
    Posts
    892
    Mentioned
    10 Post(s)
    Tagged
    0 Thread(s)
    We have a template for the widget. Next step is filling the template with data. Our helper select elements should be populated with corresponding values.

    The constructor function will also initialize the widget now.

    The init method will hide the target element and it also produces the helper elements.

    The addHelperElements method will also add select data.

    For this purpose, three quite similar getXList methods are added to the prototype (next step: refactoring) and the HELPER_ELEMENTS_HTML_FRAGMENT constant is now a template with placeholders a la Mustache.

    date.html
    Code:
    <!doctype html>
      <html>
      <head>
        <meta charset="utf-8">
        <title>Date widget</title>
      </head>
    
      <body>
        <div class="dateDropdown">
          <label for="startDate">Please enter the start date:</label>
          <input id="startDate" type="text" placeholder="dd.mm.yyyy"/>
        </div>
    
        <div class="dateDropdown">
          <label for="endDate">Please enter the end date:</label>
          <input id="endDate" type="text" placeholder="dd.mm.yyyy"/>
        </div>
        
        <script src="date.js"></script>
        <script src="demo.js"></script>
      </body>
    </html>
    date.js
    Code:
    function MyDate(id) {
        this.targetElement = document.getElementById(id);
        this.init();
    };
    
    MyDate.prototype = {
      
        MONTHS: ['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sept','Oct','Nov','Dec'],
      
        YEAR_OFFSET: 21,
    
        HELPER_ELEMENTS_HTML_FRAGMENT: '<fieldset class="mydate"><select class="day">{{dd}}</select><select class="month">{{mm}}</select><select class="year">{{yyyy}}</select></fieldset>',
    
        init: function () {
            this.toggle();
            this.addHelperElements();
        },
      
        toggle: function () {
            var elemStyle = this.targetElement.style;
            
            if (elemStyle.display === "none") {
                elemStyle.display = "";
            } else {
                elemStyle.display = "none";
            };
        },
        
        addHelperElements: function () {
            var today = this.getToday(),
                yyyy, mm, dd, fragment;
    
            yyyy = this.getYearsList(today.year);
            mm = this.getMonthsList(today.month);
            dd = this.getDaysList(today.year, today.month);
            
            fragment = this.HELPER_ELEMENTS_HTML_FRAGMENT
                .split('{{yyyy}}').join(yyyy)
                .split('{{mm}}').join(mm)
                .split('{{dd}}').join(dd);
    
            this.targetElement.insertAdjacentHTML('afterend', fragment);
        },
    
        getToday: function () {
            var today = new Date();
          
            return {
                day: today.getUTCDate(),
                month: today.getUTCMonth(),
                year: today.getUTCFullYear()
            }; 
        },
    
        getYearsList: function (year) {
            var yearsList;
    
            for(var i = 0; i < this.YEAR_OFFSET; i++){
                var y = year + i;
                yearsList = yearsList + '<option value="' + y + '">' +  y + '</option>';
            };
    
            return yearsList;
        },
    
        getMonthsList: function () {
            var monthsList;
    
            for(var i = 0; i < 12; i++){
                var m = this.MONTHS[i];
                monthsList = monthsList + '<option value="' + m + '">' +  m + '</option>';
            };
    
            return monthsList;
        },
    
        getDaysList: function (year, month) {
            var n = this.getDaysInMonth(year, month),
                daysList;
    
            for(var i = 0; i < n; i++){
                var d = i + 1;
                daysList = daysList + '<option value="' + d  + '">' +  d + '</option>';
            };
    
            return daysList;
        },
      
        getDaysInMonth: function (year, month) {
            return new Date(year, month, 0).getDate();
        }
    };
    demo.js
    Code:
    var sdat = new MyDate("startDate"),
        edat = new MyDate("endDate");

  11. #36
    Gre aus'm Pott gold trophysilver trophybronze trophy
    Pullo's Avatar
    Join Date
    Jun 2007
    Location
    Germany
    Posts
    5,938
    Mentioned
    214 Post(s)
    Tagged
    12 Thread(s)
    Looking good

    The code is easy to read and that is a nice trick with the handlebars (and the split/join).

    I also got to the point that I thought that I had three getXList methods (although in my case it was three addX methods) and thought that they should probably be refactored.

    I will be interested to see how you approach that.

  12. #37
    Non-Member
    Join Date
    Feb 2012
    Posts
    892
    Mentioned
    10 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by Pullo View Post
    I will be interested to see how you approach that.
    Tomorrow I'm getting at it. Let's return to your Ruby analogy.

    In JavaScript, a "classical" inheritance model looks like this:
    Code:
    function Parent() {};
    
    Parent.prototype.parent_method = function () {
        console.log("Parent method");
    };
    
    function Child() {};
    
    Child.prototype = Object.create(Parent.prototype);
    Child.prototype.constructor = Child;
    
    Child.prototype.child_method = function () {
        console.log("Child method");
    };
    
    var p = new Parent();
    p.parent_method();
    
    var c = new Child();
    c.child_method();
    c.parent_method();
    In this model the child inheritance is a bit awkward to code, but it's closest to the JavaScript way. To note that there is no ancestors method to follow the inheritance chain: the prototype.


    Using only objects, without constructor functions, one could do this:
    Code:
    var parent = {
        parent_method: function () {
            console.log("Parent method");
        }
    };
    
    var child = Object.create(parent);
    
    child.child_method = function () {
        console.log("Child method");
    };
    
    var grandchild = Object.create(child);
    
    grandchild.grandchild_method = function () {
        console.log("Grandchild method");
    };
    
    var p = Object.create(parent);
    p.parent_method();
    
    var c = Object.create(child);
    c.child_method();
    c.parent_method();
    
    var g = Object.create(grandchild);
    g.grandchild_method();
    g.child_method();
    g.parent_method();
    This model is cleaner to code but it doesn't use the prototype. This means that any method later added to the parent, will not be available by default to child objects.


    The future ES6 could support something like this:
    Code:
    class Parent {
        parent_method() {
            console.log("Parent method");
        }
    };
    
    class Child extends Parent {
        child_method() {
            console.log("Child method");
        }
    };
    
    var p = new Parent;
    p.parent_method();
    
    var c = new Child;
    c.child_method();
    c.parent_method();

  13. #38
    Non-Member
    Join Date
    Feb 2012
    Posts
    892
    Mentioned
    10 Post(s)
    Tagged
    0 Thread(s)
    I've combined the three getXList methods into a single getHelperLists method. I've also added the selected attribute so that the widget starts with the today date selected.

    The refactoring was an exercise. Personally, I would keep the three separate methods. The combined one is horrible readability-wise. I could expand the ternary operators into if or switch statements, but it would still be somewhat difficult to follow, and to maintain, of course.

    date.html and demo.js are the same.

    date.js
    Code:
    function MyDate(id) {
        this.targetElement = document.getElementById(id);
        this.init();
    };
    
    MyDate.prototype = {
      
        MONTHS: ['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sept','Oct','Nov','Dec'],
      
        YEAR_OFFSET: 21,
    
        HELPER_ELEMENTS_HTML_FRAGMENT: '<fieldset class="mydate"><select class="day">{{dd}}</select><select class="month">{{mm}}</select><select class="year">{{yyyy}}</select></fieldset>',
    
        init: function () {
            this.toggle();
            this.addHelperElements();
        },
      
        toggle: function () {
            var elemStyle = this.targetElement.style;
            
            if (elemStyle.display === "none") {
                elemStyle.display = "";
            } else {
                elemStyle.display = "none";
            };
        },
        
        addHelperElements: function () {
            var today = this.getToday(),
                lists = this.getHelperLists(today),
                fragment;
            
            fragment = this.HELPER_ELEMENTS_HTML_FRAGMENT
                .split('{{yyyy}}').join(lists.yyyy)
                .split('{{mm}}').join(lists.mm)
                .split('{{dd}}').join(lists.dd);
    
            this.targetElement.insertAdjacentHTML('afterend', fragment);
        },
    
        getToday: function () {
            var today = new Date();
          
            return { dd: today.getUTCDate(), mm: today.getUTCMonth(), yyyy: today.getUTCFullYear() }; 
        },
        
        getHelperLists: function (dayObj) {
            var counter = [this.YEAR_OFFSET, this.MONTHS.length, this.getDaysInMonth(dayObj.yyyy, dayObj.mm)],
                lists = {};
            
            for (var j = 0, listName; j < counter.length; j++) {
                listName = (j === 0) ? "yyyy" : (j === 1) ? "mm" : "dd";
    
                for(var i = 0, optionValue, optionSelected; i < counter[j]; i++){
                    optionValue = (j === 0) ? dayObj.yyyy + i : (j === 1) ? this.MONTHS[i] : i + 1;
                    optionSelected = ( (listName !== "dd" && dayObj[listName] === i) ? "selected" : (dayObj[listName] === i + 1) ? "selected" : "" );
                    lists[listName] = lists[listName] + '<option value="' + optionValue + '"' + optionSelected + '>' +  optionValue + '</option>';
                };
            };
            
            return lists;
        },
      
        getDaysInMonth: function (year, month) {
            return new Date(year, month, 0).getDate();
        }
    };
    Next step: event hooks. With those, the basic widget is complete.

    After that: AMD.

  14. #39
    Gre aus'm Pott gold trophysilver trophybronze trophy
    Pullo's Avatar
    Join Date
    Jun 2007
    Location
    Germany
    Posts
    5,938
    Mentioned
    214 Post(s)
    Tagged
    12 Thread(s)
    Thanks for the concise explanation. That really helps.

    One question:

    Quote Originally Posted by myty View Post
    This model is cleaner to code but it doesn't use the prototype. This means that any method later added to the parent, will not be available by default to child objects.
    Could you clarify this a little.
    This seems to work:

    Code:
    var parent = {
        parent_method: function () {
            console.log("Parent method");
        },
    };
    
    var child = Object.create(parent);
    
    var c = Object.create(child);
    c.parent_method();
    
    // Method added later
    parent.random_method = function(){
      console.log("Random method");
    }
    
    c.random_method();
    
    => "Random method"

  15. #40
    Gre aus'm Pott gold trophysilver trophybronze trophy
    Pullo's Avatar
    Join Date
    Jun 2007
    Location
    Germany
    Posts
    5,938
    Mentioned
    214 Post(s)
    Tagged
    12 Thread(s)
    Quote Originally Posted by myty View Post
    The refactoring was an exercise. Personally, I would keep the three separate methods. The combined one is horrible readability-wise. I could expand the ternary operators into if or switch statements, but it would still be somewhat difficult to follow, and to maintain, of course.
    You're not kidding
    Readability wise I much prefer the previous version.
    Also, if the conditions change (e.g. it should be possible to select past years) it would be a nightmare to update six months down the road.

    It's also good to see that this was a challenge for you to do.
    I spent a long time messing around with this code last night, but the main sticking point was the fact that the option value and option text was entirely different for each case.
    I came up with a solution which had a separate function call to get these and although that shortened the over all code, it was so ugly I just binned it.

    Quote Originally Posted by myty View Post
    After that: AMD.
    :homer voice: Woohoo!

  16. #41
    Non-Member
    Join Date
    Feb 2012
    Posts
    892
    Mentioned
    10 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by Pullo View Post
    Readability wise I much prefer the previous version.
    Not so long ago JavaScript terseness was a must. These days I like readability better.

  17. #42
    Non-Member
    Join Date
    Feb 2012
    Posts
    892
    Mentioned
    10 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by Pullo View Post
    Could you clarify this a little.
    This seems to work
    You're right, it works. I just got a little side-tracked with the difference between __proto__ and prototype. Thanks for catching this.

  18. #43
    Non-Member
    Join Date
    Feb 2012
    Posts
    892
    Mentioned
    10 Post(s)
    Tagged
    0 Thread(s)
    OK, I've added a delegated click event for the fieldset. It will check all clicks for the child elements, and it will replace the days list when year or month change.

    Basically, a dayResetter goes into the "class" prototype, meaning the MyDate instances, for the onClick event, they will have a reference to this method, rather then every instance holding the logic for the event. This makes the prototype inheritance so cool.

    Beside the bindUIActions method, which is the entry point for click events and other possible events, I've separate again the code logic, in three getXList methods. After AMDing this, I'm thinking about adding LoDash functional features to the project for these methods. Seems like a natural fit.

    date.html and demo.js remain the same.

    date.js
    Code:
    function MyDate(id) {
        this.targetElement = document.getElementById(id);
        this.init();
    };
    
    MyDate.prototype = {
      
        MONTHS: ['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sept','Oct','Nov','Dec'],
      
        YEAR_OFFSET: 21,
    
        HELPER_ELEMENTS_HTML_FRAGMENT: '<fieldset class="mydate"><select class="day">{{day}}</select><select class="month">{{month}}</select><select class="year">{{year}}</select></fieldset>',
    
        init: function () {
            this.toggle();
            this.addHelperElements();
            this.bindUIActions();
        },
      
        toggle: function () {
            var elemStyle = this.targetElement.style;
            
            if (elemStyle.display === "none") {
                elemStyle.display = "";
            } else {
                elemStyle.display = "none";
            };
        },
        
        addHelperElements: function () {
            var today = this.getToday(),
                lists = this.getHelperLists(today),
                fragment = '';
            
            fragment = this.HELPER_ELEMENTS_HTML_FRAGMENT
                .split('{{year}}').join(lists.years)
                .split('{{month}}').join(lists.months)
                .split('{{day}}').join(lists.days);
    
            this.targetElement.insertAdjacentHTML('afterend', fragment);
        },
    
        getToday: function () {
            var today = new Date();
            
            return {
                day: today.getUTCDate(),
                month: today.getUTCMonth(),
                year: today.getUTCFullYear()
            };
        },
        
        getHelperLists: function (dayObj) {
            return {
                years: this.getYearsList(dayObj),
                months: this.getMonthsList(dayObj),
                days: this.getDaysList(dayObj)
            };
        },
        
        getYearsList: function (dayObj) {
            var years = '';
            
            for(var i = 0; i < this.YEAR_OFFSET; i++){
                var y = dayObj.year + i;
                years = years + '<option value="' 
                              + y 
                              + '"'
                              + ( (i === dayObj.year) ? "selected" : "" )
                              + '>' 
                              +  y 
                              + '</option>';
            };
            
            return years;
        },
        
        getMonthsList: function (dayObj) {
            var months = '';
            
            for(var i = 0; i < this.MONTHS.length; i++){
                var m = this.MONTHS[i];
                months = months + '<option value="' 
                                + m 
                                + '"'
                                + ( (dayObj.month === i) ? "selected" : "" )
                                + '>' 
                                +  m 
                                + '</option>';
            };
            
            return months;
        },
        
        getDaysList: function (dayObj) {
            var days = '';
            
            for(var i = 0, n = this.getDaysInMonth(dayObj.year, dayObj.month); i < n; i++){
                var d = i + 1;
                days = days + '<option value="' 
                            + d  
                            + '"'
                            + ( (dayObj.day === i + 1) ? "selected" : "" )
                            + '>' 
                            +  d 
                            + '</option>';
            };
            
            return days;
        },
      
        getDaysInMonth: function (year, month) {
            return new Date(year, month, 0).getDate();
        },
        
        bindUIActions: function () {
            var dateWidgets = document.getElementsByClassName('mydate');
            
            for (var i = 0; i < dateWidgets.length; i++) {
                dateWidgets[i].onchange = this.dayResetter;
            };
        },
        
        dayResetter: function (event) {        
            var allSelects,
                yearSelect, monthSelect, daySelect,
                year = '', month = '',
                fragment = '';
            
            event.preventDefault();
            
            if (event.target.className !== 'day') {
                allSelects = this.getElementsByTagName('select');
                
                yearSelect = allSelects[2];
                monthSelect = allSelects[1];
                daySelect = allSelects[0];
                
                year = yearSelect.options[yearSelect.selectedIndex].text;
                month = monthSelect.selectedIndex+1;
                
                fragment = '<select class="day">' + MyDate.prototype.getDaysList( {year: year, month: month} ) + '</select>';
                
                daySelect.innerHTML = '';
                daySelect.insertAdjacentHTML('afterBegin', fragment);
            }
        }
    };

  19. #44
    Gre aus'm Pott gold trophysilver trophybronze trophy
    Pullo's Avatar
    Join Date
    Jun 2007
    Location
    Germany
    Posts
    5,938
    Mentioned
    214 Post(s)
    Tagged
    12 Thread(s)
    Great job!

    Quote Originally Posted by myty View Post
    Basically, a dayResetter goes into the "class" prototype, meaning the MyDate instances, for the onClick event, they will have a reference to this method, rather then every instance holding the logic for the event. This makes the prototype inheritance so cool.
    Do you mean that in my version, each "month select" and each "year select", in each instance of the date widget had its own copy of the click handler, whereas in your version they all reference the same prototype method?

    Quote Originally Posted by myty View Post
    Beside the bindUIActions method, which is the entry point for click events and other possible events, I've separate again the code logic, in three getXList methods.
    I like that. It makes it much easier to read.

    Also, +1 for no jQuery.

  20. #45
    Non-Member
    Join Date
    Feb 2012
    Posts
    892
    Mentioned
    10 Post(s)
    Tagged
    0 Thread(s)
    Yes, I'm only putting references in the on*event* properties for the elements, you are filling them with actual function code.

    $(".month") creates a HTMLCollection containing element references, and so does the $(".year"). It's a display of functional programming available with jQuery, where it loops over a collection, a list, and automatically performs actions for each item in the collection.

    The jQuery .on() method attaches event handlers to the elements. Mine attaches references, yours attaches actual code, duplicated code for every one of them.


    You could improve your original code in tow ways, like I did: 1) use a delegation event for the div class="dateDropdown" parent element to check for select changes and 2) by creating a prototype method. When I call the method, it's not found directly on the instance, but it gets looked up and it gets found up the prototype chain.

    EXAMPLE
    The prototype chain mechanism is how you can call [].slice.
    [] is an Array instance, but the slice() method is in Array.prototype.
    Prototype mechanism goes and looks for the method up the prototype chain for [] and finds it in Array.prototype for every array you create, instead of every array you create having the same duplicated slice() method.

  21. #46
    Gre aus'm Pott gold trophysilver trophybronze trophy
    Pullo's Avatar
    Join Date
    Jun 2007
    Location
    Germany
    Posts
    5,938
    Mentioned
    214 Post(s)
    Tagged
    12 Thread(s)
    Great example.
    That makes a lot of sense.
    Thank you.

  22. #47
    Non-Member
    Join Date
    Feb 2012
    Posts
    892
    Mentioned
    10 Post(s)
    Tagged
    0 Thread(s)
    You're welcome.

  23. #48
    Non-Member
    Join Date
    Feb 2012
    Posts
    892
    Mentioned
    10 Post(s)
    Tagged
    0 Thread(s)
    Here's the github repo for the AMDed MyDate widget.
    https://github.com/itmitica/myDate

    For now, it's still monolithic. I simply added the require library to the mix. This should give you an idea on how to start converting old code to AMD.

    In the following steps I'm going to tear the code apart.

  24. #49
    Gre aus'm Pott gold trophysilver trophybronze trophy
    Pullo's Avatar
    Join Date
    Jun 2007
    Location
    Germany
    Posts
    5,938
    Mentioned
    214 Post(s)
    Tagged
    12 Thread(s)
    Hi myty,

    I cloned the repo and had a poke around it.
    Interesting stuff!

    I haven't had the occasion to do much with require.js or AMD.
    I'll have a read of the docs in the next couple of days to get up to speed.

  25. #50
    Non-Member
    Join Date
    Feb 2012
    Posts
    892
    Mentioned
    10 Post(s)
    Tagged
    0 Thread(s)
    Hey Pullo.

    I've committed another change. I've separated the constructor and the prototype from the myDate class file.
    https://github.com/itmitica/myDate/c...fbe64890526284

    Next step: breaking up the prototype.


Bookmarks

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •