SitePoint Sponsor

User Tag List

Results 1 to 17 of 17
  1. #1
    SitePoint Member
    Join Date
    May 2013
    Posts
    12
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)

    Adding buttons using onclick function. Need to limit buttons to 10 HELP

    Code:
    <Div ID=files>  
      <br>
        
      File 1 : <input type="file" name="File1"><br>    
      
    </Div>
    <br> 
    
    <Input Type=Button Value="Add a file" OnClick=return(Expand()) 
     Style="border=0;background=yellow;cursor:hand"><br>
        
    <p align="center"> 
      
    <br>
       
    <input type="submit" name="submit" value="Upload Images">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;   
    </Form>
    
    <% trying to control this area%>
    <%if nfiles reaches 10 then function is disabled%>
    
    <Script>
    //Expand form with a new File fields if needed.
    var nfiles = 1;
    function Expand(){
      nfiles++
      var adh = '<BR> File '+nfiles+' : <input type="file" name="File'+nfiles+'">';
      files.insertAdjacentHTML('BeforeEnd',adh);
      return false;
    }
    </Script>
    Last edited by Pullo; Jul 5, 2013 at 22:39. Reason: Added code tags

  2. #2
    Gre aus'm Pott gold trophysilver trophybronze trophy
    Pullo's Avatar
    Join Date
    Jun 2007
    Location
    Germany
    Posts
    5,999
    Mentioned
    219 Post(s)
    Tagged
    12 Thread(s)
    Hi,

    Maybe I'm missing something obvious, but couldn't you do this:

    Code JavaScript:
    var nfiles = 1;
    function Expand(){
      if (nfiles <= 10){
        var adh = '<BR> File '+nfiles+' : <input type="file" name="File'+nfiles+'">';
        files.insertAdjacentHTML('BeforeEnd',adh);
        nfiles++
      }
      return false;
    }

  3. #3
    SitePoint Guru
    Join Date
    Sep 2006
    Posts
    731
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    If you must use innerHTML:
    Code:
    <!DOCTYPE html>
    <head>
    <style>
    img{ width:30px; height:30px; border:solid 4px red}
    </style>
    </head>
    <html>
    
    <form>
    
    <div ID=files>  
      <br>    
      File 1 : <input type="file" name="File1"><br>    
      
    </div>
    <br> 
    
    <Input Type=Button Value="Add a file" OnClick="this.disabled = !Expand( 10 )" 
     style="border=0;background=yellow;cursor:hand">
     <br>
     <p align="center"> 
    <br>
       
    <input type="submit" name="submit" value="Upload Images">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;   
    </form>
    
    <% trying to control this area%>
    <%if nfiles reaches 10 then function is disabled%>
    
    <script>
    
    function Expand( limit )
    {
      var formDiv = document.getElementById( 'files' ),
          nFiles = formDiv.getElementsByTagName( 'input' ).length;
      
      if( nFiles < limit )    
      {
        var adh = '<BR> File ' + ( nFiles + 1 ) + ' : <input type="file" name="File'+ (nFiles + 1) + '"><br>';
      
        formDiv.insertAdjacentHTML( 'beforeend', adh );
      }
      
      return formDiv.getElementsByTagName( 'input' ).length < limit;
    }
    </script>
    
    </body>
    </html>
    Tab-indentation is a crime against humanity.

  4. #4
    Gre aus'm Pott gold trophysilver trophybronze trophy
    Pullo's Avatar
    Join Date
    Jun 2007
    Location
    Germany
    Posts
    5,999
    Mentioned
    219 Post(s)
    Tagged
    12 Thread(s)
    Hi Ali,

    Quote Originally Posted by Logic Ali View Post
    If you must use innerHTML:
    Just for my personal interest, why do you consider it bad practice to use innerHTML?

  5. #5
    SitePoint Member
    Join Date
    May 2013
    Posts
    12
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Thanks Guys, this worked great

  6. #6
    SitePoint Guru
    Join Date
    Sep 2006
    Posts
    731
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by Pullo View Post
    Hi Ali,



    Just for my personal interest, why do you consider it bad practice to use innerHTML?
    It allows more opportunity to introduce syntax errors that may not be easily detected.
    Tab-indentation is a crime against humanity.

  7. #7
    Gre aus'm Pott gold trophysilver trophybronze trophy
    Pullo's Avatar
    Join Date
    Jun 2007
    Location
    Germany
    Posts
    5,999
    Mentioned
    219 Post(s)
    Tagged
    12 Thread(s)
    Quote Originally Posted by Logic Ali View Post
    It allows more opportunity to introduce syntax errors that may not be easily detected.
    So would you recommend using createElement, createTextNode and appendChild and co?

    Code:
    <!DOCTYPE HTML>
    <html>
      <head>
        <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
        <title>Unbenanntes Dokument</title>
        <style>
          .center{ margin:0 auto; }
          #addButton{ cursor:pointer }
          #submitButton, #addButton { margin: 10px 0; display: block; }
          #files > div { margin: 15px 0; }
        </style>
      </head>
      
      <body>
        <form>
          <div id="files">  
            <div>
              File 1:
              <input type="file" name="file_1">
            </div>
          </div>
          <input type="button" id="addButton" value="Add a file" />
          <input type="submit" id="submitButton" name="submit" value="Upload Images">
        </form>
        
        <script>
          var addButton = document.getElementById( 'addButton' );
          addButton.onclick = function()
          {
            this.disabled = !expand( 10 );
          }
          
          function expand( limit )
          {
            var formDiv = document.getElementById( 'files' ),
                nFiles = formDiv.getElementsByTagName( 'input' ).length;
            
            if( nFiles < limit )    
            {
              var div = document.createElement( 'div' ),
                  input = document.createElement( 'input' );
                  
              div.appendChild(document.createTextNode( 'File ' + (nFiles + 1) + ': ' ));
              formDiv.appendChild( div );
              
              input.type = 'file';
              input.name = 'file_' + nFiles + 1;
              div.appendChild( input );
              
              formDiv.appendChild( div );
            }
            
            return formDiv.getElementsByTagName( 'input' ).length < limit;
          }
        </script>
      </body>
    </html>

  8. #8
    Unobtrusively zen silver trophybronze trophy
    paul_wilkins's Avatar
    Join Date
    Jan 2007
    Location
    Christchurch, New Zealand
    Posts
    14,729
    Mentioned
    104 Post(s)
    Tagged
    4 Thread(s)
    Quote Originally Posted by Pullo View Post
    Just for my personal interest, why do you consider it bad practice to use innerHTML?
    Historically there have been some reservations about using innerHTML because it's not supported with XHTML code, and innerHTML is unofficial - it's not backed by specs of any sort.

    Since IE refused to support XHTML, that cause for the issue is now moot, and we now have HTML5 specs for innerHTML.

    I don't favour using innerHTML in my own code, perhaps because I'm a crotchety old bugger who aims to do things that are right (morally), but using innerHTML certainly is more and more tempting now.

    There is only one good reason that I know of now to not use innerHTML, and that's due to it bring HTML code in to your JavaScript, for which a separation of concerns should be maintained if at all possible.
    Programming Group Advisor
    Reference: JavaScript, Quirksmode Validate: HTML Validation, JSLint
    Car is to Carpet as Java is to JavaScript

  9. #9
    SitePoint Guru
    Join Date
    Sep 2006
    Posts
    731
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by Pullo View Post
    So would you recommend using createElement, createTextNode and appendChild and co?
    Yes, but only within a function written to simplify the whole process required.
    Tab-indentation is a crime against humanity.

  10. #10
    Unobtrusively zen silver trophybronze trophy
    paul_wilkins's Avatar
    Join Date
    Jan 2007
    Location
    Christchurch, New Zealand
    Posts
    14,729
    Mentioned
    104 Post(s)
    Tagged
    4 Thread(s)
    Quote Originally Posted by Pullo View Post
    So would you recommend using createElement, createTextNode and appendChild and co?
    I would recommend using a template. Where the HTML structure is maintained as hidden elements on the page, and the script retrieves that template and uses it as a basis for which to update the page.

    We will soon have a <template> tag too. A good read about them can be found in this article on HTML's New Template Tag

    As a measure of backwards-compatibility, we can also use a smattering of CSS to hide such templates when making use of them now with our code.

    Code css:
    template { display: none; }

    I'll come up with an example of using them with the above code, sometime tomorrow as I have to shortly head away offline.
    Programming Group Advisor
    Reference: JavaScript, Quirksmode Validate: HTML Validation, JSLint
    Car is to Carpet as Java is to JavaScript

  11. #11
    Unobtrusively zen silver trophybronze trophy
    paul_wilkins's Avatar
    Join Date
    Jan 2007
    Location
    Christchurch, New Zealand
    Posts
    14,729
    Mentioned
    104 Post(s)
    Tagged
    4 Thread(s)
    Quote Originally Posted by paul_wilkins View Post
    I'll come up with an example of using them with the above code, sometime tomorrow as I have to shortly head away offline.
    In fact, I can do it now.

    We can add a template for the files section, anywhere on the HTML page but I would choose to place it just at the start of the <body> tag

    HTML Code:
    <body>
    <template id="fileinput">File <span></span> : <input type="file" name="File1"><br></template>
    ...
    The empty span allows us to easily find it from the script, and place what we need in to that location.

    For backwards compatibility with web browser that don't know how to handle templates, we have some CSS and JavaScript that hide such templates and a polyfill to help set things up:

    HTML Code:
    <script>
        // Shim so we can style in IE6/7/8
        document.createElement('template');
    </script>
    Code css:
    template { display: none; }

    HTML Code:
    <script src="template-polyfill.js"></script>
    which contains the following code:

    Code javascript:
    /* POLYFILL */
     
    (function templatePolyfill(d) {
        if('content' in d.createElement('template')) {
            return false;
        }
     
        var qPlates = d.getElementsByTagName('template'),
            plateLen = qPlates.length,
            elPlate,
            qContent,
            contentLen,
            docContent;
     
        for(var x=0; x<plateLen; ++x) {
            elPlate = qPlates[x];
            qContent = elPlate.childNodes;
            contentLen = qContent.length;
            docContent = d.createDocumentFragment();
     
            while(qContent[0]) {
                docContent.appendChild(qContent[0]);
            }
     
            elPlate.content = docContent;
        }
    })(document);

    And we are now ready to start making use of that template.

    Here's a simple function that retrieves the template and creates some new content from it:

    Code javascript:
    function createFileInput(num) {
        var template = document.getElementById('fileinput');
     
        fileInput = template.content.cloneNode(true);
        fileInput.querySelector('span').appendChild(document.createTextNode(num));
        fileInput.querySelector('input').name = 'File' + (num);
        return fileInput;
    }

    And we can use that from the script as follows:

    Code javascript:
    filesDiv.appendChild(createFileInput(nFiles + 1));

    @Pullo or @Logic-Ali - do you feel like experimenting with that while I'm away?
    Programming Group Advisor
    Reference: JavaScript, Quirksmode Validate: HTML Validation, JSLint
    Car is to Carpet as Java is to JavaScript

  12. #12
    Gre aus'm Pott gold trophysilver trophybronze trophy
    Pullo's Avatar
    Join Date
    Jun 2007
    Location
    Germany
    Posts
    5,999
    Mentioned
    219 Post(s)
    Tagged
    12 Thread(s)
    Hi Paul,

    Great answer.
    I knew (vaguely) about the new <template> tag, but it was fun to use it in context.

    Quote Originally Posted by paul_wilkins View Post
    @Pullo or @Logic-Ali - do you feel like experimenting with that while I'm away?
    Aye, why not?
    JavaScript is inline, so that the OP can copy and paste the code if desired.

    Code:
    <!DOCTYPE HTML>
    <html>
      <head>
        <!--createElement, createTextNode and appendChild-->
        <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
        <title>Dynamically add buttons (using template)</title>
        <script>
          // Shim so we can style in IE6/7/8
          document.createElement('template');
        </script>
        <style>
          .center{ margin:0 auto; }
          #addButton{ cursor:pointer }
          #submitButton, #addButton { margin: 10px 0; display: block; }
          #files > div { margin: 15px 0; }
          template { display: none; }
        </style>
      </head>
      
      <body>
        <form>
          <div id="files">  
            <div>
              File 1:
              <input type="file" name="file_1">
            </div>
          </div>
          <input type="button" id="addButton" value="Add a file" />
          <input type="submit" id="submitButton" name="submit" value="Upload Images">
        </form>
        
        <template id="fileinput">
          <div>
            <span>File </span>
            <input type="file">
          </div>
        </template>
        
        <script>
          (function templatePolyfill(d) {
            if('content' in d.createElement('template')) {
              return false;
            }
            
            var qPlates = d.getElementsByTagName('template'),
                plateLen = qPlates.length,
                elPlate,
                qContent,
                contentLen,
                docContent;
            
            for(var x=0; x<plateLen; ++x) {
              elPlate = qPlates[x];
              qContent = elPlate.childNodes;
              contentLen = qContent.length;
              docContent = d.createDocumentFragment();
              
              while(qContent[0]) {
                docContent.appendChild(qContent[0]);
              }
              
              elPlate.content = docContent;
            }
          })(document);
    
          var addButton = document.getElementById('addButton');
          addButton.onclick = function(){
            this.disabled = !expand(10);
          }
          
          function createFileInput(num) {
              var template = document.getElementById('fileinput');
           
              fileInput = template.content.cloneNode(true);
              fileInput.querySelector('span').appendChild(document.createTextNode(num + ':'));
              fileInput.querySelector('input').name = 'File' + (num);
              return fileInput;
          }
          
          function expand(limit){
            var formDiv = document.getElementById('files'),
                nFiles = formDiv.getElementsByTagName('input').length;
            
            if(nFiles < limit) {
              formDiv.appendChild(createFileInput(nFiles + 1));
            }
            
            return formDiv.getElementsByTagName('input').length < limit;
          }
        </script>
      </body>
    </html>
    This works down to IE8, but IE7 balks, as it doesn't like the use of querySelector.
    As per our JS challenge I was thinking about the best way to polyfill this functionality.

    I tried including Sizzle (http://cdnjs.cloudflare.com/ajax/lib.../sizzle.min.js), but in this case you have to pass Sizzle the context as a seperate parameter

    e.g.

    Code JavaScript:
    Sizzle('span', fileInput)

    Which means that things start to get a bit messy.
    Can you think of a more elegant way to make this work in IE7?

  13. #13
    Unobtrusively zen silver trophybronze trophy
    paul_wilkins's Avatar
    Join Date
    Jan 2007
    Location
    Christchurch, New Zealand
    Posts
    14,729
    Mentioned
    104 Post(s)
    Tagged
    4 Thread(s)
    Quote Originally Posted by Pullo View Post
    Which means that things start to get a bit messy.
    Can you think of a more elegant way to make this work in IE7?
    The following is a good polyfill for querySelector and querySelectorAll support:

    Code javascript:
    // Selectors API Level 1 ([url]http://www.w3.org/TR/selectors-api/[/url])
      // [url]http://ajaxian.com/archives/creating-a-queryselector-for-ie-that-runs-at-native-speed[/url]
      //
      if (!document.querySelectorAll) {
        document.querySelectorAll = function (selectors) {
          var style = document.createElement('style'), elements = [], element;
          document.documentElement.firstChild.appendChild(style);
          document._qsa = [];
     
          style.styleSheet.cssText = selectors + '{x-qsa:expression(document._qsa && document._qsa.push(this))}';
          window.scrollBy(0, 0);
          style.parentNode.removeChild(style);
     
          while (document._qsa.length) {
            element = document._qsa.shift();
            element.style.removeAttribute('x-qsa');
            elements.push(element);
          }
          document._qsa = null;
          return elements;
        };
      }
     
      if (!document.querySelector) {
        document.querySelector = function (selectors) {
          var elements = document.querySelectorAll(selectors);
          return (elements.length) ? elements[0] : null;
        };
      }
     
      if (!document.getElementsByClassName) {
        document.getElementsByClassName = function (classNames) {
          classNames = String(classNames).replace(/^|\s+/g, '.');
          return document.querySelectorAll(classNames);
        };
      }

    Something that's also worth considering is the aspect of how much additional code to include for the sake of backwards compatibility.

    It's also possible to easily achieve things without needing querySelector, for example by using getElementsByTagName:

    Code javascript:
    function createFileInput(num) {
        var template = document.getElementById('fileinput'),
            fileInput = template.content.cloneNode(true),
            span = fileInput.getElementsByTagName('span')[0],
            input = fileInput.getElementsByTagName('input')[0];
     
        span.appendChild(document.createTextNode(num + ':'));
        input.name = 'File' + (num);
        return fileInput;
    }

    Although, more complex situations are easier when using querySelector, so that can be a good reason to include the polyfill.
    Programming Group Advisor
    Reference: JavaScript, Quirksmode Validate: HTML Validation, JSLint
    Car is to Carpet as Java is to JavaScript

  14. #14
    Gre aus'm Pott gold trophysilver trophybronze trophy
    Pullo's Avatar
    Join Date
    Jun 2007
    Location
    Germany
    Posts
    5,999
    Mentioned
    219 Post(s)
    Tagged
    12 Thread(s)
    Hi Paul,

    I've been playing about with this for a while now, but unfortunately I cannot get the querySelectorAll pollyfill to work.
    I know it's not strictly necessary, as the same functionality could be acheived using getElementsByTagName, but nonetheless I would be interested to hear your thoughts.

    The exact error I receive is:

    Code:
    SCRIPT438: Object doesn't support property or method "querySelector"
    HTML5_template.html, Line 112 Character 11
    in the emulated version of IE7 (real version IE10).

    Code:
    <!DOCTYPE HTML>
    <html>
      <head>
        <!--createElement, createTextNode and appendChild-->
        <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
        <title>Dynamically add buttons (using template)</title>
        <script>
          // Shim so we can style in IE6/7/8
          document.createElement('template');
        </script>
        <style>
          .center{ margin:0 auto; }
          #addButton{ cursor:pointer }
          #submitButton, #addButton { margin: 10px 0; display: block; }
          #files > div { margin: 15px 0; }
          template { display: none; }
        </style>
      </head>
      
      <body>
        <form>
          <div id="files">  
            <div>
              File 1:
              <input type="file" name="file_1">
            </div>
          </div>
          <input type="button" id="addButton" value="Add a file" />
          <input type="submit" id="submitButton" name="submit" value="Upload Images">
        </form>
        
        <template id="fileinput">
          <div>
            <span>File </span>
            <input type="file">
          </div>
        </template>
        
        <script>
          // Selectors API Level 1 (http://www.w3.org/TR/selectors-api/)
          // http://ajaxian.com/archives/creating...t-native-speed
          //
          if (!document.querySelectorAll) {
            document.querySelectorAll = function (selectors) {
              var style = document.createElement('style'), elements = [], element;
              document.documentElement.firstChild.appendChild(style);
              document._qsa = [];
         
              style.styleSheet.cssText = selectors + '{x-qsa:expression(document._qsa && document._qsa.push(this))}';
              window.scrollBy(0, 0);
              style.parentNode.removeChild(style);
         
              while (document._qsa.length) {
                element = document._qsa.shift();
                element.style.removeAttribute('x-qsa');
                elements.push(element);
              }
              document._qsa = null;
              return elements;
            };
          }
         
          if (!document.querySelector) {
            document.querySelector = function (selectors) {
              var elements = document.querySelectorAll(selectors);
              return (elements.length) ? elements[0] : null;
            };
          }
         
          if (!document.getElementsByClassName) {
            document.getElementsByClassName = function (classNames) {
              classNames = String(classNames).replace(/^|\s+/g, '.');
              return document.querySelectorAll(classNames);
            };
          }    
            
          (function templatePolyfill(d) {
            if('content' in d.createElement('template')) {
              return false;
            }
            
            var qPlates = d.getElementsByTagName('template'),
                plateLen = qPlates.length,
                elPlate,
                qContent,
                contentLen,
                docContent;
            
            for(var x=0; x<plateLen; ++x) {
              elPlate = qPlates[x];
              qContent = elPlate.childNodes;
              contentLen = qContent.length;
              docContent = d.createDocumentFragment();
              
              while(qContent[0]) {
                docContent.appendChild(qContent[0]);
              }
              
              elPlate.content = docContent;
            }
          })(document);
    
          var addButton = document.getElementById('addButton');
          addButton.onclick = function(){
            this.disabled = !expand(10);
          }
          
          function createFileInput(num) {
              var template = document.getElementById('fileinput');
           
              fileInput = template.content.cloneNode(true);
              fileInput.querySelector('span').appendChild(document.createTextNode(num + ':'));
              fileInput.querySelector('input').name = 'File' + (num);
              return fileInput;
          }
          
          function expand(limit){
            var formDiv = document.getElementById('files'),
                nFiles = formDiv.getElementsByTagName('input').length;
            
            if(nFiles < limit) {
              formDiv.appendChild(createFileInput(nFiles + 1));
            }
            
            return formDiv.getElementsByTagName('input').length < limit;
          }
        </script>
      </body>
    </html>
    Any ideas?

  15. #15
    Unobtrusively zen silver trophybronze trophy
    paul_wilkins's Avatar
    Join Date
    Jan 2007
    Location
    Christchurch, New Zealand
    Posts
    14,729
    Mentioned
    104 Post(s)
    Tagged
    4 Thread(s)
    Quote Originally Posted by Pullo View Post
    Any ideas?
    Yes, that's happening because IE7 doesn't have an Elements object from which to inherit things like querySelector.
    You may find it easier to experiment in a more recent IE with the F12 debug screen set as IE7.

    I suspect that the only solution for compatibility there is to refrain from using querySelector on partial searches, and use things like getElementsByTagName instead.

    For example:

    Code javascript:
    fileInput.getElementsByTagName('span')[0].appendChild(document.createTextNode(num + ':'));
    fileInput.getElementsByTagName('input')[0].name = 'File' + (num);

    It may also be feasible to use a separate function for the task, such as get.one(fileInput, 'span', 'tag') which can attempt the querySelector method, and if it fails then attempt other compatible methods.

    It's always the case though that the further back you go, the more difficult it is to retain modern techniques, and have to resort to facades behind which the work gets done.
    Programming Group Advisor
    Reference: JavaScript, Quirksmode Validate: HTML Validation, JSLint
    Car is to Carpet as Java is to JavaScript

  16. #16
    Unobtrusively zen silver trophybronze trophy
    paul_wilkins's Avatar
    Join Date
    Jan 2007
    Location
    Christchurch, New Zealand
    Posts
    14,729
    Mentioned
    104 Post(s)
    Tagged
    4 Thread(s)
    Due to how IE7 and earlier have troubles with querySelector on unattached elements, I thought that I had found an effective way by adding the content from the template to the page first, then querying the page for that added section. But to no avail.

    If you want compatibility with IE7 and earlier, we are restricted to using querySelector to retrieve elements from the page, and should only use querySelector to attain references from the already existing document.

    Which is fine - getElementsByTagName and other methods are just perfect for editing the clones template.
    Programming Group Advisor
    Reference: JavaScript, Quirksmode Validate: HTML Validation, JSLint
    Car is to Carpet as Java is to JavaScript

  17. #17
    Gre aus'm Pott gold trophysilver trophybronze trophy
    Pullo's Avatar
    Join Date
    Jun 2007
    Location
    Germany
    Posts
    5,999
    Mentioned
    219 Post(s)
    Tagged
    12 Thread(s)
    Thanks!
    I've just been going over this now and it has helped me formulate my thoughts a bit more clearly on backwards compatibility.


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
  •