Hello and welcome to our community! Is this your first visit?
Register
Enjoy an ad free experience by logging in. Not a member yet? Register.
Results 1 to 10 of 10
  1. #1
    New Coder
    Join Date
    Aug 2005
    Location
    Earth
    Posts
    39
    Thanks
    0
    Thanked 0 Times in 0 Posts

    Reactivating a "document function"

    Hey all, slight issue, would be no problem with jQuery but in this example I can't use it.

    Basically I have a lovely script that filters out rows of a table based on whether or not they have a cell containing the data in an input field.


    So basically anything not in the 'filter' text field will be removed from the 'order-table' table below. All is well and good, the problem now is that I've added a 'Reverse Filter' checkbox, so that rows will disappear if they DON'T match the text field. The checkbox ID is 'rev-filter' and you can see the if statement in the middle of the JS.


    Now this does actually work. I can type something in the filter text field and it'll filter them out, the problem is if I click the 'Reverse Filter' checkbox, I want the changes to be immediate. Instead, I have to do a keypress in the text field for the table to update, whereas I want the table to update as soon as I click the checkbox but nothing I try works. Have I made sense and can anyone help?
    Last edited by Petsmacker; 05-14-2016 at 04:50 PM.

  2. #2
    Master Coder felgall's Avatar
    Join Date
    Sep 2005
    Location
    Sydney, Australia
    Posts
    8,131
    Thanks
    3
    Thanked 814 Times in 803 Posts
    The first code fragment you posted is in the wrong order - JavaScript comes just before the </body> tag - not higher in the page.

    The second code fragment is nonsense - don't jumble JavaScript with HTML. With the properly written (if misplaced) JavaScript in your first piece of code there is no way for the garbage JavaScript in the second fragment to interact with it as they are in completely different scopes.

    You haven't posted the entire code for the form. How the JavaScript is to interact with the form depends on how the form is coded.
    Stephen
    Learn Modern JavaScript - http://javascriptexample.net/
    Helping others to solve their computer problem at http://www.felgall.com/

    Don't forget to start your JavaScript code with "use strict"; which makes it easier to find errors in your code.

  3. #3
    New Coder
    Join Date
    Aug 2005
    Location
    Earth
    Posts
    39
    Thanks
    0
    Thanked 0 Times in 0 Posts
    I live for the day I can be as indignant as you about Javascript, sir.

    I didn't include the head/body tags and everything else because quite simply, they aren't actually that relevant. Yes to be pedantic I could post a W3C compliant webpage but frankly, the snippet works and is relevant without and that's all I need help with. The script still works all in one go if you paste the below into a .html file. Furthermore, inline Javascript launching as an event isn't yet punishable by death.
    Last edited by Petsmacker; 05-14-2016 at 04:50 PM.

  4. #4
    Senior Coder xelawho's Avatar
    Join Date
    Nov 2010
    Posts
    3,482
    Thanks
    57
    Thanked 633 Times in 628 Posts
    If you would have no problem coding it in jQuery, why not do that then translate it back to vanilla? There's nothing magical about jQuery and nothing it can do that vanilla can't

  5. #5
    Senior Coder
    Join Date
    Aug 2010
    Posts
    1,123
    Thanks
    30
    Thanked 250 Times in 248 Posts
    Code:
    <input type="search" class="light-table-filter" data-table="order-table" id="filter" placeholder="Filter"> 
    <label><input type="checkbox"  id="rev-filter" value="1">Reverse Filter</label>
    <table class="order-table">
    <tr>
    <td>a</td>
    </tr><tr>
    <td>b</td>
    </tr><tr>
    <td>c</td>
    </tr>
    </table>
    <script>
    (function() {
       'use strict';
      var LightTableFilter = (function(Arr) {
        var _input;
        function _onInputEvent(e) {
          _input = filter;
          var tables = document.getElementsByClassName(_input.getAttribute('data-table'));
          Arr.forEach.call(tables, function(table) {
            Arr.forEach.call(table.tBodies, function(tbody) {
              Arr.forEach.call(tbody.rows, _filter);
            });
          });
        }
        function _filter(row) {
          var text = row.textContent.toLowerCase(),
            val = _input.value.toLowerCase();
          if (document.getElementById("rev-filter").checked == true) {
            row.style.display = text.indexOf(val) > 0 ? 'none' : 'table-row';
          } else {
            row.style.display = text.indexOf(val) === -1 ? 'none' : 'table-row';
          }
        }
        return {
          init: function() {
            var inputs = document.getElementsByClassName('light-table-filter');
            Arr.forEach.call(inputs, function(input) {
              input.oninput = _onInputEvent;
    
    
       document.getElementById("rev-filter").onclick= _onInputEvent;
            });
          }
        };
      })(Array.prototype);  
     LightTableFilter.init(); 
    })();
    </script>
    Quote Originally Posted by Petsmacker View Post
    Code:
    <label><input type="checkbox" onclick="javascript:WHATGOESHERE???" id="rev-filter" value="1">Reverse Filter</label>
    as Stephen
    has said "nothingGoesThe​re"
    If your elements were in a form
    you could replace this ,,,
    _input = filter;
    with code that would reference
    the form then it could be reused
    for other forms in the page
    script goes at bottom
    Last edited by DaveyErwin; 05-14-2016 at 02:36 AM.
    Cleverness is serviceable for
    everything,
    sufficient in nothing.

  6. #6
    Master Coder felgall's Avatar
    Join Date
    Sep 2005
    Location
    Sydney, Australia
    Posts
    8,131
    Thanks
    3
    Thanked 814 Times in 803 Posts
    Quote Originally Posted by Petsmacker View Post
    I didn't include the head/body tags and everything else because quite simply, they aren't actually that relevant.
    The form you want to have interact with the JavaScript IS relevant and you didn't post the complete form and so the exact JavaScript to interact with the form can't be determined as not enough of the actual HTML is supplied.

    We need everything from the <form> to the </form> to be able to determine how JavaScript needs to interact with <input> tags. The only form fields allowed outside of form tags are <button> tags. The HTML needs to be valid for the JavaScript to work.
    Last edited by felgall; 05-14-2016 at 04:05 AM.
    Stephen
    Learn Modern JavaScript - http://javascriptexample.net/
    Helping others to solve their computer problem at http://www.felgall.com/

    Don't forget to start your JavaScript code with "use strict"; which makes it easier to find errors in your code.

  7. #7
    New Coder
    Join Date
    Aug 2005
    Location
    Earth
    Posts
    39
    Thanks
    0
    Thanked 0 Times in 0 Posts
    Quote Originally Posted by xelawho View Post
    If you would have no problem coding it in jQuery, why not do that then translate it back to vanilla? There's nothing magical about jQuery and nothing it can do that vanilla can't
    This is part of a script that:
    a) is on server that could well not have an internet connection
    b) Needs to be lightweight


    There is also no form on the page, what I posted above is literally 90% of the page that I have anyway. Perhaps reference by document.getelementbyID possible? The only form-related elements are the text field and the checkbox.

    The Javascript DOES work, it just needs to work on the click of a checkbox rather than the keydown in the text field. I think what I'd better do is just rewrite the JS into a function that can be referenced by onKeyDown for the text field and onClick for the checkbox and that way it'll execute every time either of those events happens.

  8. #8
    New Coder
    Join Date
    Aug 2005
    Location
    Earth
    Posts
    39
    Thanks
    0
    Thanked 0 Times in 0 Posts
    Just to prove a point, this is literally the author's codepen with the code, most of which is the same as mine: https://codepen.io/chriscoyier/pen/tIuBL
    Last edited by Petsmacker; 05-14-2016 at 07:20 AM.

  9. #9
    New Coder
    Join Date
    Aug 2005
    Location
    Earth
    Posts
    39
    Thanks
    0
    Thanked 0 Times in 0 Posts
    Solved, rewrote the script and I prefer my one, thanks for the help!
    Last edited by Petsmacker; 05-14-2016 at 04:50 PM.

  10. #10
    Regular Coder
    Join Date
    Feb 2016
    Location
    Keene, NH
    Posts
    365
    Thanks
    1
    Thanked 50 Times in 47 Posts
    Some advice... rather than screwing around with readystatechange, just put the script before </body> and just run the attach then. By that point the DOM is always built, so you don't need to play around with events like onload, domdocumentready or readystatechange, just do it!

    Likewise, try NOT to do lookups or nested references inside your events. They take up a good deal of time, processing power (therein battery life) which is why a lot of such scripting enhancements are unpopular with the mobile crowd. (which is why more and more mobile users are blocking JS)

    If you were to store an array of the rows to be filtered in a data attribute (well, data_ since get/setAttribute are a PITA) you can do a lot of the heavy lifting at load time instead of on every keypress. The only reason to process every time is if you are adding/removing rows from the scripting -- and in that case I'd regenerate the list on add/remove/repopulate and not on keystroke.

    Something like...
    Code:
    (function(d) {
    
    	'use strict';
    
    	function inputEvent(e) {
    		for (
    			var 
    				value = e.target.value.toLowerCase(),
    				i = 0, iLen = e.target.data_targetRows.length;
    			i < iLen; i++
    		) e.target.data_targetRows[i].style.display = 
    			e.target.data_targetRows[i].textContent.toLowerCase().indexOf(value) === -1 ?
    			'none' :
    			'table-row';
    	}
    	
    	for (
    		var
    			inputs = d.getElementsByClassName('light-table-filter'),
    			i = 0, iLen = inputs.length;
    		i < iLen; i++
    	) {
    		inputs[i].data_targetRows = [];
    		inputs[i].addEventListener('input', inputEvent);
    		for (
    			var
    				tables = d.getElementsByClassName(inputs[i].getAttribute('data-table')),	
    				j = 0, jLen = tables.length;
    			j < jLen; j++
    		) for (
    			var
    				table = tables[j],
    				k = 0, kLen = table.tBodies.length;
    			k < kLen; k++
    		) for (
    			var
    				rows = table.tBodies[k].rows,
    				l = 0, lLen = rows.length;
    			l < lLen; l++
    		) inputs[i].data_targetRows.push(rows[l])
    	}
    	
    })(document);
    (untested, might be typo's but it should work)

    In my version I create variable references to fixed values during the loop initializer, which prevents "long" array lookups on every loop. Amazingly declaring the new variable reference to an array index is faster inside the loop than looking it up every time -- same reason you'll see scripts (like mine) that store array lengths before looping too. I use "for" instead of "Array.forEach" as the overhead of the function calls (requring a good deal of stack dicking around) also slows down execution a LOT, even if it seems like less code/less typing.

    I also reduced the number of functions -- that filter operation being in it's own function was cute, but it's just more overhead from another unnecessary function call. Unless you were calling that filter from multiple places, don't waste the overhead of a function on it!

    I also prefer to keep things in call order; I realize JavaScript has "lifting" but I don't trust it.

    I was going to go ahead and polyfill for legacy, but given the trigger event is "input", something that doesn't even exist in IE8/earlier there's really no reason to bother. STILL I'd consider trapping "keypress" and "onchange" instead (yes, both) with a timeout event for two reasons:

    1) If someone is typing as fast as I do, you're not hammering the scripting for nothing. A 200 to 300ms delay before filtering can make an amazing drop in the amount of CPU chewed up by the scripting.

    2) legacy browsers can trap onchange and keypress, and by the time a timeout() passes the event will have been processed. When a new event occurs you check if there's already a timeout counting down, if so you delete it... then either way create a new one. The trick would be passing the event to that timeout, which is why I'd put the timeout's handler and the element it is for into an object indexed by your data-table attribute.

    Oh, and are you REALLY sure you'd have more than one table being filtered by this script? If you're only indexing one table axing the outer loop and using an ID instead would make more sense and use less overhead.

    -------------

    edit -- PS, i realize this is just a demo, but in production try to remember that a placeholder is not a label!
    From time to time the accessibility of websites must be refreshed with the blood of designers and owners; it is its natural manure.
    http://www.cutcodedown.com


 

Tags for this Thread

Posting Permissions

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