0

I'm learning to utilize pure JS in Django projects. Being a server-side developer the concepts are relatively new for me, so need a hand.

Imagine a form where a user can post text, and/or upload an image. Think of it as a simple Django form like so:

class PostForm(forms.Form):
    image = forms.ImageField(required=False)
    reply = forms.CharField(required=False, widget=forms.Textarea(attrs={'cols':30,'rows':3,'class': 'cxl','autocomplete': 'off','autofocus': 'autofocus'}))

    def __init__(self,*args,**kwargs):
        super(PostForm, self).__init__(*args,**kwargs)
        self.fields['image'].widget.attrs['id'] = 'browse_image_btn'

    def clean(self):
        data = self.cleaned_data
        # perform cleaning
        return data

If I were to render this form in HTML, something like the following would do:

<form action="{% url 'post_content' %}" method="POST" enctype="multipart/form-data">
    {% csrf_token %}
    {{ post_form.reply }}<br>
    {{ post_form.image }}
    <label for="browse_image_btn" style="cursor: pointer;"><img alt="Select photo" src="{{ STATIC_URL }}img/upload.svg" width="70" height="70"></label>        
    <input type="submit" value="submit">
</form>

But how do I create this HTML solely via JS?

Use-case: imagine a page that contains a long list of content, with a reply button under each. The form above only appears under specific content once the user has pressed the reply button. Moreover, pressing the same button toggles it off too.

I can't seem to wrap my head around solving a problem like that. Please advise, preferably with an illustrative example. I'd prefer to understand the most efficient, scalable way to do this, one that can be deployed in professional settings. Only pure JS, no JQuery.


I've made attempts at this, but they're half-baked since I'm a JS newbie. Here's what I've got so far:

  var okBtn = document.createElement('button');
    setUpSubmitBtn(okBtn);
   e.target.parentNode.insertAdjacentElement('afterend', okBtn);

   var camBtn = document.createElement('img');
    setUpCameraBtn(camBtn);
   e.target.parentNode.insertAdjacentElement('afterend', lineBreak);
   e.target.parentNode.insertAdjacentElement('afterend', camBtn);

   var replyBox = document.createElement('textarea');
    setUpReplyBox(replyBox);
   e.target.parentNode.insertAdjacentElement('afterend', replyBox);

Where there are simple JS functions to create each of the widgets referred above:

function setUpReplyBox(replyBox) {
    replyBox.classList.add('mts');
    replyBox.setAttribute('id', 'reply-message');
    replyBox.setAttribute('style', 'display:inline');
    replyBox.setAttribute('placeholder', 'Reply');
}

function setUpSubmitBtn(okBtn) {
    okBtn.classList.add('btn', 'bcb', 'bs', 'mts');
    okBtn.setAttribute('style','border:none;height:25px;display:inline;');
    okBtn.setAttribute('id','reply-ok');
    okBtn.innerHTML = "OK";
}

function setUpCameraBtn(camBtn) {
    camBtn.setAttribute('id','camera');
    camBtn.setAttribute('src','/static/img/cam1.svg');
    camBtn.setAttribute('width','45');
    camBtn.setAttribute('height','45');
    camBtn.setAttribute('style','display:inline;float:right;');
}

I feel like I'm doing it the hard/wrong way. The right pattern is probably going to be much simpler.

2
  • Just render all forms with display none and toggle them with js or render one and move it with js (change id dynamicly) Commented Jan 28, 2018 at 1:19
  • @bigless: I was just wondering if rendering all forms with display:none, would work. I like your 2nd suggestion even more; regarding rendering one and moving it with JS. But how would that work? Would I do position: absolute; top: -9999px; left: -9999px;, and then move it in place with JS? It would be great to get a quick starter example from you on this subject. Commented Jan 28, 2018 at 1:29

1 Answer 1

1

Hope it helps:

var formTemplate = document.querySelector('#form-template form');
var idInput = document.getElementById('id-input');

function toggleReply(e) {
  if (!formTemplate) return;

  e.target.parentNode.insertBefore(formTemplate, e.target);
  idInput.value = e.target.getAttribute('data-id');
};

Array.from(document.querySelectorAll('button.reply'))
  .forEach(btn => btn.onclick = toggleReply)
form + .reply {
  display: none;
}
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet"/>

<div id="form-template" style="display: none;">
  <form action="" method="POST" enctype="multipart/form-data">
      <input id="id-input" type="hidden" name="id" value="">
      <div class="form-group">
        <label for="reply">Reply:</label>
        <textarea class="form-control" type="text" name="text"></textarea>
      </div>
      <input type="submit" class="btn btn-default pull-right" value="submit">
  </form>
</div>

<div class="container">
  <div class="list-group">
    <div class="panel panel-default">
      <div class="panel-heading">
        <h3 class="panel-title">Comment title</h3>
      </div>
      <div class="panel-body">
        Comment content
      </div>
      <div class="panel-footer clearfix">
        <button class="reply btn btn-default pull-right" type="button" data-id="24">Reply</button>
      </div>
    </div>
    <div class="panel panel-default">
      <div class="panel-heading">
        <h3 class="panel-title">Comment title</h3>
      </div>
      <div class="panel-body">
        Comment content
      </div>
      <div class="panel-footer clearfix">
        <button class="reply btn btn-default pull-right" type="button" data-id="25">Reply</button>
      </div>
    </div>
  </div>
</div>

Sign up to request clarification or add additional context in comments.

8 Comments

Thanks for this, I'm going through this. One thing though: the exercise I'm doing is pure JS, not JQuery. Could you change the JQuery code to JS instead?
@HassanBaig updated but remember that manupulating DOM with vanilla could be real pain.
Thanks for that. As a learning goal, I was thinking I'd learn pure JS concepts and then move onto the more powerful JQuery. However, there's quite a buzz around me re: React.JS. What would be your advice regarding which library to get accustomed to for someone who wants to improve their client-side chops?
@HassanBaig Note that I am not fun of jQuery but if you want dynamic frontend without rewriting backend, its fastest way. I dont know ReactJS but guess its frontend mvc same as angular. Its good for sites where are expected many user interactions like admins etc. Con is need to write restapi.
The form kept within the div with id form-template is inserted in a different location once a reply button is pressed. If a second reply button is pressed, this same form moves again. However, by then, that form wasn't inside the div with id form-template any more (it had already moved once). Then how on earth does var formTemplate = document.querySelector('#form-template form'); get populated correctly every time? I thought #form-template form meant select the form inside the element with id form-template. Would love to get some explanation regarding that added to your answer
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.