maxxd Posted August 9, 2016 Share Posted August 9, 2016 Hey y'all, I've got a weird one here and was hoping someone had a word or two of wisdom. I've got a php script that outputs a couple banks of checkboxes in a dynamic form. I've also written a JavaScript script that will sort through the form elements on page load and hide a form element, replacing it with a setup of divs that I can then style. I've got the JS working to emulate form element interaction depending on form element type and state.To cut way back (I hope) on the TL;DR quotient, I've cut the code down pretty significantly below. Hopefully it still makes sense.Here's the JS: ;function StylishForms(frm){ "use strict"; var _form, _overlay, _elementHelpers = {}, _elementTotals = { 'checkbox' : 0, 'selects' : 0, 'text' : 0, }; kickOff(frm); function kickOff(frm){ if(typeof frm === 'string'){ _form = document.forms[frm]; }else{ if(typeof _form.jquery !== 'undefined'){ _form = document.forms[_form.attr('id')]; }else{ _form = frm; } } for(var i in _form.elements){ console.log(_form.elements[i].type + ' : ' + _form.elements[i].name); } } } Second JS file: (function($){ if($('form').length > 0){ $('form').each(function(){ StylishForms( $(this) ); }); } }) (jQuery); And the HTML: <form action="" method="post" class="wpcf7-form testing-forms" novalidate="novalidate" id="test_1"> <p> <span class="wpcf7-form-control-wrap selector"> <select name="selector" class="wpcf7-form-control wpcf7-select selector" id="testing" aria-invalid="false"> <option value="first option">first option</option> <option value="option 1">option 1</option> <option value="option 2">option 2</option> <option value="option 3">option 3</option> <option value="option 4">option 4</option> <option value="option 5">option 5</option> <option value="option 6">option 6</option> <option value="option 7">option 7</option> </select> </span> </p> <p> <span class="wpcf7-form-control-wrap secondselect"> <select name="secondselect" class="wpcf7-form-control wpcf7-select" id="test_more" aria-invalid="false"> <option value="Second Option 1">Second Option 1</option> <option value="Second Option 2">Second Option 2</option> <option value="Second Option 3" selected="selected">Second Option 3</option> <option value="Second Option 4">Second Option 4</option> </select> </span> </p> <p> <span class="wpcf7-form-control-wrap checkTester"> <span class="wpcf7-form-control wpcf7-checkbox" id="checkTesterOption"> <span class="wpcf7-list-item first"> <input type="checkbox" name="checkTester[]" value="Option 1" /> <span class="wpcf7-list-item-label">Option 1</span> </span> <span class="wpcf7-list-item"> <input type="checkbox" name="checkTester[]" value="Option 2" /> <span class="wpcf7-list-item-label">Option 2</span> </span> <span class="wpcf7-list-item last"> <input type="checkbox" name="checkTester[]" value="Option 3" /> <span class="wpcf7-list-item-label">Option 3</span> </span> </span> </span> <br /> <span class="wpcf7-form-control-wrap checkTester2"> <span class="wpcf7-form-control wpcf7-checkbox" id="checkTesterOption2"> <span class="wpcf7-list-item first last"> <input type="checkbox" name="checkTester2[]" value="Option 2-1" /> <span class="wpcf7-list-item-label">Option 2-1</span> </span> </span> </span> </p> <p> <span class="wpcf7-form-control-wrap text_testing"> <input type="text" name="text_testing" value="" size="40" class="wpcf7-form-control wpcf7-text" aria-invalid="false" /> </span> </p> <p> <input type="submit" value="submit" class="wpcf7-form-control wpcf7-submit" /> </p> </form> And finally, the console output: select-one : selector select-one : secondselect undefined : undefined undefined : undefined undefined : undefined checkbox : checkTester2[] text : text_testing submit : The select and text objects work exactly as expected across OS's and browsers - it's the checkboxes that are the issue. In Firefox, Opera, and Chrome on Windows, Mac, IOS, and Android they work as expected. In Safari on IOS and Mac, everything works as expected. On IE 10, 11, or Edge (obviously on Windows), they fail almost entirely. You can see from the console output that the only time Edge reports a checkbox is when there's only one associated with the name (in this case, checkTester2[]). If there's more than one checkbox, it reports both the input type and name as undefined.As much as I'd love to simply say "people using MS-based browsers need to stop doing that", unfortunately I can't. But I also can't find what I'm missing here. Anyone see anything that I don't? Any and all help is much appreciated. Quote Link to comment https://forums.phpfreaks.com/topic/301805-microsoft-browers-and-checkbox-form-elements/ Share on other sites More sharing options...
Jacques1 Posted August 10, 2016 Share Posted August 10, 2016 How about using explicit indexes so that the names are in fact different? <input type="checkbox" name="checkTester[0]" value="Option 1"> <input type="checkbox" name="checkTester[1]" value="Option 2"> <input type="checkbox" name="checkTester[2]" value="Option 3"> The resulting PHP array won't be as “pretty”, because unchecked boxes will lead to index gaps. But that should be entirely irrelevant and can easily be fixed with array_values() if necessary. Quote Link to comment https://forums.phpfreaks.com/topic/301805-microsoft-browers-and-checkbox-form-elements/#findComment-1535871 Share on other sites More sharing options...
maxxd Posted August 10, 2016 Author Share Posted August 10, 2016 How about using explicit indexes so that the names are in fact different? <input type="checkbox" name="checkTester[0]" value="Option 1"> <input type="checkbox" name="checkTester[1]" value="Option 2"> <input type="checkbox" name="checkTester[2]" value="Option 3"> The resulting PHP array won't be as “pretty”, because unchecked boxes will lead to index gaps. But that should be entirely irrelevant and can easily be fixed with array_values() if necessary. Very much worth exploring - thanks for the idea. Unfortunately, it's a WordPress site and contact form plugin, so I'm not entirely sure it's possible, but I'm definitely going to check it out now - thanks again! Quote Link to comment https://forums.phpfreaks.com/topic/301805-microsoft-browers-and-checkbox-form-elements/#findComment-1535872 Share on other sites More sharing options...
maxxd Posted August 10, 2016 Author Share Posted August 10, 2016 OK - given a little experimenting, it appears your suggestion will work Jacques1 - much thanks for the suggestion. Now I just need to figure out how to add the explicit indexes to the plugin output. woot. Quote Link to comment https://forums.phpfreaks.com/topic/301805-microsoft-browers-and-checkbox-form-elements/#findComment-1535879 Share on other sites More sharing options...
requinix Posted August 10, 2016 Share Posted August 10, 2016 Did I hear somebody ask for an analysis? No? TOO BAD! Demo: <html><body> <form action="" method="post" class="wpcf7-form testing-forms" novalidate="novalidate" id="test_1"> <p> <span class="wpcf7-form-control-wrap selector"> <select name="selector" class="wpcf7-form-control wpcf7-select selector" id="testing" aria-invalid="false"> <option value="first option">first option</option> <option value="option 1">option 1</option> <option value="option 2">option 2</option> <option value="option 3">option 3</option> <option value="option 4">option 4</option> <option value="option 5">option 5</option> <option value="option 6">option 6</option> <option value="option 7">option 7</option> </select> </span> </p> <p> <span class="wpcf7-form-control-wrap secondselect"> <select name="secondselect" class="wpcf7-form-control wpcf7-select" id="test_more" aria-invalid="false"> <option value="Second Option 1">Second Option 1</option> <option value="Second Option 2">Second Option 2</option> <option value="Second Option 3" selected="selected">Second Option 3</option> <option value="Second Option 4">Second Option 4</option> </select> </span> </p> <p> <span class="wpcf7-form-control-wrap checkTester"> <span class="wpcf7-form-control wpcf7-checkbox" id="checkTesterOption"> <span class="wpcf7-list-item first"> <input type="checkbox" name="checkTester[]" value="Option 1" /> <span class="wpcf7-list-item-label">Option 1</span> </span> <span class="wpcf7-list-item"> <input type="checkbox" name="checkTester[]" value="Option 2" /> <span class="wpcf7-list-item-label">Option 2</span> </span> <span class="wpcf7-list-item last"> <input type="checkbox" name="checkTester[]" value="Option 3" /> <span class="wpcf7-list-item-label">Option 3</span> </span> </span> </span> <br /> <span class="wpcf7-form-control-wrap checkTester2"> <span class="wpcf7-form-control wpcf7-checkbox" id="checkTesterOption2"> <span class="wpcf7-list-item first last"> <input type="checkbox" name="checkTester2[]" value="Option 2-1" /> <span class="wpcf7-list-item-label">Option 2-1</span> </span> </span> </span> </p> <p> <span class="wpcf7-form-control-wrap text_testing"> <input type="text" name="text_testing" value="" size="40" class="wpcf7-form-control wpcf7-text" aria-invalid="false" /> </span> </p> <p> <input type="submit" value="submit" class="wpcf7-form-control wpcf7-submit" /> </p> </form> <pre> </pre> <script type="text/javascript"> function typeofex(value) { var t = typeof(value); return t == "object" && value["constructor"] && value["constructor"]["name"] ? t + "(" + value.constructor.name + ")" : t; } var pre = document.getElementsByTagName("pre")[0]; for (var i in document.forms) { for (var j in document.forms[i].elements) { pre.innerHTML = pre.innerHTML + "forms[" + i + "].elements[" + j + "] is a " + typeofex(document.forms[i].elements[j]) + "\n" + ".name = '" + document.forms[i].elements[j].name + "'\n" + ".type = '" + document.forms[i].elements[j].type + "'\n"; } } </script> </body></html>Output from Chrome 51.0.2704.103: forms[0].elements[0] is a object(HTMLSelectElement) .name = 'selector' .type = 'select-one' forms[0].elements[1] is a object(HTMLSelectElement) .name = 'secondselect' .type = 'select-one' forms[0].elements[2] is a object(HTMLInputElement) .name = 'checkTester[]' .type = 'checkbox' forms[0].elements[3] is a object(HTMLInputElement) .name = 'checkTester[]' .type = 'checkbox' forms[0].elements[4] is a object(HTMLInputElement) .name = 'checkTester[]' .type = 'checkbox' forms[0].elements[5] is a object(HTMLInputElement) .name = 'checkTester2[]' .type = 'checkbox' forms[0].elements[6] is a object(HTMLInputElement) .name = 'text_testing' .type = 'text' forms[0].elements[7] is a object(HTMLInputElement) .name = '' .type = 'submit' forms[0].elements[testing] is a object(HTMLSelectElement) .name = 'selector' .type = 'select-one' forms[0].elements[selector] is a object(HTMLSelectElement) .name = 'selector' .type = 'select-one' forms[0].elements[test_more] is a object(HTMLSelectElement) .name = 'secondselect' .type = 'select-one' forms[0].elements[secondselect] is a object(HTMLSelectElement) .name = 'secondselect' .type = 'select-one' forms[0].elements[checkTester[]] is a object(RadioNodeList) .name = 'undefined' .type = 'undefined' forms[0].elements[checkTester2[]] is a object(HTMLInputElement) .name = 'checkTester2[]' .type = 'checkbox' forms[0].elements[text_testing] is a object(HTMLInputElement) .name = 'text_testing' .type = 'text' forms[0].elements[namedItem] is a function .name = 'namedItem' .type = 'undefined' forms[0].elements[length] is a number .name = 'undefined' .type = 'undefined' forms[0].elements[item] is a function .name = 'item' .type = 'undefined' forms[test_1].elements[0] is a object(HTMLSelectElement) .name = 'selector' .type = 'select-one' forms[test_1].elements[1] is a object(HTMLSelectElement) .name = 'secondselect' .type = 'select-one' forms[test_1].elements[2] is a object(HTMLInputElement) .name = 'checkTester[]' .type = 'checkbox' forms[test_1].elements[3] is a object(HTMLInputElement) .name = 'checkTester[]' .type = 'checkbox' forms[test_1].elements[4] is a object(HTMLInputElement) .name = 'checkTester[]' .type = 'checkbox' forms[test_1].elements[5] is a object(HTMLInputElement) .name = 'checkTester2[]' .type = 'checkbox' forms[test_1].elements[6] is a object(HTMLInputElement) .name = 'text_testing' .type = 'text' forms[test_1].elements[7] is a object(HTMLInputElement) .name = '' .type = 'submit' forms[test_1].elements[testing] is a object(HTMLSelectElement) .name = 'selector' .type = 'select-one' forms[test_1].elements[selector] is a object(HTMLSelectElement) .name = 'selector' .type = 'select-one' forms[test_1].elements[test_more] is a object(HTMLSelectElement) .name = 'secondselect' .type = 'select-one' forms[test_1].elements[secondselect] is a object(HTMLSelectElement) .name = 'secondselect' .type = 'select-one' forms[test_1].elements[checkTester[]] is a object(RadioNodeList) .name = 'undefined' .type = 'undefined' forms[test_1].elements[checkTester2[]] is a object(HTMLInputElement) .name = 'checkTester2[]' .type = 'checkbox' forms[test_1].elements[text_testing] is a object(HTMLInputElement) .name = 'text_testing' .type = 'text' forms[test_1].elements[namedItem] is a function .name = 'namedItem' .type = 'undefined' forms[test_1].elements[length] is a number .name = 'undefined' .type = 'undefined' forms[test_1].elements[item] is a function .name = 'item' .type = 'undefined'Output with Edge on Windows 10 build 10586.494: forms[test_1].elements[selector] is a object(HTMLSelectElement) .name = 'selector' .type = 'select-one' forms[test_1].elements[secondselect] is a object(HTMLSelectElement) .name = 'secondselect' .type = 'select-one' forms[test_1].elements[checkTester[]] is a object(HTMLCollection) .name = 'undefined' .type = 'undefined' forms[test_1].elements[checkTester2[]] is a object(HTMLInputElement) .name = 'checkTester2[]' .type = 'checkbox' forms[test_1].elements[text_testing] is a object(HTMLInputElement) .name = 'text_testing' .type = 'text' forms[test_1].elements[ms__id261] is a object(HTMLInputElement) .name = '' .type = 'submit' forms[test_1].elements[length] is a number .name = 'undefined' .type = 'undefined' forms[test_1].elements[item] is a function .name = 'item' .type = 'undefined' forms[test_1].elements[namedItem] is a function .name = 'namedItem' .type = 'undefined' Chrome: - elements provides iteration over its Collection numerically and associatively: by index in the collection, by id if given, and by name if given. This seems to apply to all Collections. - Accessing duplicate elements in the Collection by name gives another Collection Edge: - elements provides iteration over its Collection associatively by name, using a placeholder value if none is given - Accessing duplicate elements in the Collection by name gives another Collection - Iteration over the Collection does not expose numeric keys, however numeric keys to elements are accepted So actually the behavior you're seeing in IE/Edge is also present in Chrome, but is hidden by the fact that you still get the information you need as its provided via the numeric keys. What doesn't help this is that the HTML/DOM standards don't seem to indicate precisely how iteration should work. At this point I would offer an alternative to your for..in but you haven't said what you're trying to do with this code. Truthfully, since it surfaces all members of an object, for..in actually provides an attack surface for use with XSS and is thus generally discouraged without the developer taking proper protections. 1 Quote Link to comment https://forums.phpfreaks.com/topic/301805-microsoft-browers-and-checkbox-form-elements/#findComment-1535888 Share on other sites More sharing options...
maxxd Posted August 10, 2016 Author Share Posted August 10, 2016 @requinix - thank you for the analysis! What I'm doing with the script is basically replacing form elements with individual series of divs that act as those form elements. So, a checkbox (in this case) is replaced with a simple div and an onclick() function that toggles the underlying (and hidden) checkbox state according to the display state of the div. Display is controlled by adding and removing classes (checked and unchecked), while the checkbox state is controlled via the .checked property of the checkbox in question. The two are linked via a JS-injected data-attribute on the div that contains the ID of the corresponding checkbox. Now that the word 'checkbox' has little meaning any more, I hope that makes sense. From what I'm seeing in your console output, it looks like I need to add a check to see if the current form element is an HTMLCollection, and if so, iterate a loop through that. So in essence, every browser except Edge and IE are flattening a multi-dimensional array of form elements. Am I reading that correctly (I've only had one cup of coffee, so that's a genuine question)? Or is it as simple as using a for..of loop? Off to try... Also, if I'm passing in a specific local form object, how does that create a potential XSS vulnerability? And again, thank you very much! Quote Link to comment https://forums.phpfreaks.com/topic/301805-microsoft-browers-and-checkbox-form-elements/#findComment-1535899 Share on other sites More sharing options...
Solution kicken Posted August 10, 2016 Solution Share Posted August 10, 2016 Using for...in with an array (or array-like object) is not preferred. There is no guarantee over the order items will be iterated in and in the case of an array-like object it may include other properties you do not want. Use a regular for loop with a numeric index. for(var i=0; i<_form.elements.length; i++){ console.log(_form.elements[i].type + ' : ' + _form.elements[i].name); } 1 Quote Link to comment https://forums.phpfreaks.com/topic/301805-microsoft-browers-and-checkbox-form-elements/#findComment-1535904 Share on other sites More sharing options...
maxxd Posted August 10, 2016 Author Share Posted August 10, 2016 Well that made things entirely too simple. Thank you. Going to sit in a corner and cry for a bit about the hours I wasted trying to 'debug' this. Quote Link to comment https://forums.phpfreaks.com/topic/301805-microsoft-browers-and-checkbox-form-elements/#findComment-1535905 Share on other sites More sharing options...
Recommended Posts
Join the conversation
You can post now and register later. If you have an account, sign in now to post with your account.