1
\$\begingroup\$

I was recently developing some canvas library and (mimicking canvas using div). The requirement was to develop something without using any third-party library.

The sample app had a asset library and a div based canvas. Asset library can contain images and text that can be placed over canvas. Also, images could be uploaded in the asset library.

I recently received feedback that the code wasn't structured properly. I used module pattern for the above task. I need some help to review the code and see what can be improved.

main.js

//DEPENDS ON ajax.js. MUST BE INCLUCED BEFORE THIS FILE.

(function() {

  let app = {};

  const api = {
    getImages: '/images',
    uploadImages: '/uploads'
  }
  const selectors = {
    getimageList: function() {
      return document.querySelector('.image ul')
    },
    getuploadBtn: function() {
      return document.querySelector('#submit')
    },
    getimageFileField: function() {
      return document.querySelector('#imageUpload')
    },
    getaddImageToCanvasBtn: function() {
      return document.querySelectorAll('.addImageToCanvas')
    },
    getCanvas: function() {
      return document.querySelector('.canvas .block')
    },
    getAddTextBtn: function() {
      return document.querySelector('#addText')
    },
    getTextField: function() {
      return document.querySelector('#textField')
    },
  }

  app.getImages = function() {
    return ajax.get(api.getImages);
  }

  app.appendImagetoList = function(imageURL) {
    selectors.getimageList().innerHTML += `<div class="image-item">
    <img src=${imageURL} class="image-list" />
    <button class="btn btn-warning btn-sm addImageToCanvas" data-imageURL=${imageURL}>Add to canvas</button>
  </div>`;
  }


  app.renderImageList = function() {
    return new Promise(function(resolve, reject) {
      app.getImages().then(function(images) {
        images.forEach(function(image) {
          app.appendImagetoList(image);
        })
        resolve();
      })
    })
  }

  app.uploadFile = function(file) {
    if (file.files.length) {
      formData = new FormData();
      formData.append('upload', file.files[0]);
      return ajax.post(api.uploadImages, formData, [{
        "Content-Type": "multipart/form-data"
      }]);
    }
  }


  app.generateDomElement = function(element, attributes) {
    let domElement = document.createElement(element);
    for (let key in attributes) {
      if (attributes.hasOwnProperty(key)) {
        domElement[key] = attributes[key];
      }
    }
    return domElement;
  }

  app.placeElementOnCanvas = function(type, element) {
    let canvas = selectors.getCanvas();
    let removeBtn = app.generateDomElement('span', {
      innerHTML: 'x',
      className: 'removeCanvasElement'
    });
    let container = app.generateDomElement('div', {
      className: type === 'image' ? 'canvasImageElementContainer' : 'canvasTextElementContainer'
    });
    let Element;
    if (type === 'image') {
      Element = app.generateDomElement('img', {
        src: element,
        className: 'canvasImageElement'
      });
    } else {
      Element = app.generateDomElement('input', {
        value: element,
        className: 'canvasTextElement'
      });
    }

    container.appendChild(Element);
    container.appendChild(removeBtn);
    canvas.appendChild(container);
    removeBtn.addEventListener('click', function() {
      container.parentNode.removeChild(container); //removes the element
    })

  }

  //attaching event listeners
  app.attachListeners = function() {

    //file upload button
    selectors.getuploadBtn().addEventListener('click', () => {
      app.uploadFile(selectors.getimageFileField()).then(function(response) {
        app.appendImagetoList(response.file);
      })
    })

    //place image to canvas button
    selectors.getaddImageToCanvasBtn().forEach(function(button) {
      button.addEventListener('click', (evnt) => {
        app.placeElementOnCanvas('image', evnt.target.dataset.imageurl);
      })
    })

    //place text to canvas button
    selectors.getAddTextBtn().addEventListener('click', (evnt) => {
      let text = selectors.getTextField().value;
      app.placeElementOnCanvas('text', text);
    });
  }

  //initializing
  app.renderImageList().then(function() {
    app.attachListeners(); //attaching listeners
  });

})();

ajax.js

// CUSTOM WRITTEN AJAX MICRO LIBRARY
// USES PROMISE BASED RETURNS
// SUPPORTS ONLY POST AND GET AS OF NOW
// USES ECMA 6 FEATURES

let ajax = (function() {
  let ajaxInterface = {} //interface that will be exported

  //CONSTRUCTING A XHR OBJECT
  let xhr; //private
  if (window.ActiveXObject) {
    try {
      xhr = new ActiveXObject("Microsoft.XMLHTTP");
    } catch (e) {
      alert(e.message);
      xhr = null;
    }
  } else {
    xhr = new XMLHttpRequest();
  }

  let makeRequest = function(method, url, headers, formData) {
    let promise = new Promise(function(resolve, reject) {
      xhr.open(method, url, true);

      //setting headers
      if (headers) {
        headers.forEach(function(header, key) {
          xhr.setRequestHeader(key, header);
        });
      }
      formData ? xhr.send(formData) : xhr.send();
      xhr.onreadystatechange = function() {
        if (xhr.readyState === 4) {
          resolve(JSON.parse(xhr.responseText));
        }
      }
    });
    return promise;
  }

  //EXPOSING THE INTERFACE FOR AJAX REQUESTS (ONLY GET AND POST ARE SUPPORTED YET)
  ajaxInterface.get = function(url, headers) {
    return makeRequest('GET', url, headers)
  }

  ajaxInterface.post = function(url, formData, headers) {
    return makeRequest('POST', url, headers, formData);
  }

  return ajaxInterface;

})()

index.html

<!DOCTYPE html>
<html>
<head>
  <title>Canvas</title>
  <link rel="stylesheet" type="text/css" href="bootstrap.css">
  <link rel="stylesheet" type="text/css" href="main.css">
</head>
<body>
<!-- side pane -->
<div class="sidepane col-sm-3 col-md-3 col-lg-3">
  <div class="form">
    <h3>Form</h3>
      <input type="file" id="imageUpload" class="form-control" placeholder="Upload Your Images" name="upload">
      <button id="submit" class="btn btn-default">upload</button>
    <!-- Upload Form here -->
  </div>
  <hr />
  <div class="assets">
    <h3>Assets</h3>
    <div class="text">
        <h4>Text</h4>
        <input type="text" id="textField">
        <button id="addText" class="btn btn-success btn-sm">Add Text</button>
    </div>
    <div class="image">
        <h4>Images</h4>
        <ul class="list-unstyled">
            <!-- List of images here -->
            <!-- <li><img src="images/sample.jpeg" class="img-rounded" /></li> -->
        </ul>
    </div>
  </div>
</div>
<!-- canvas -->
<div class="canvas col-sm-8 col-md-8 col-lg-8">
    <div class="block">
        <!-- Add images and texts to here -->
    </div>
</div>

</body>

<!-- exposes a global ajax object for making ajax requests - (Todo : Make a AMD MODULE and load with require.js) -->
<!-- MUST BE INCLUDED BEFORE main.js -->
<script type="text/JavaScript" src='js/ajax.js'></script>
<script type="text/JavaScript" src='js/main.js'></script>

</html>
\$\endgroup\$
  • \$\begingroup\$ Everyone seems to be concerned more about wordings of the question, it's description, it's grammer, it's title etc. Can't we focus more on the question asked instead? \$\endgroup\$ – beNerd Oct 14 '16 at 6:34
  • 2
    \$\begingroup\$ Welcome to Code Review. Our community cares about the quality of the questions as well as code quality, so we have guidelines — see How to Ask. \$\endgroup\$ – 200_success Oct 14 '16 at 7:58

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.