Skip to content
Advertisement

Using Javascript to implement Live Django Search

I am adding a Search functionality to my E-commerce Project using Javascript, I have followed a tutorial that explains that when writing the title in the search bar only the items with the same letter appears. In my project, it was working fine for basic HTML but I am trying to make it a little more complex to include a complete card with some details such as price, not just the title.

Here is the model.py

class Item(models.Model):
    title = models.CharField(max_length=100)
    image = models.ImageField(blank=False, upload_to=upload_design_to)
    price = models.DecimalField(decimal_places=2, max_digits=100)
    discount_price = models.DecimalField(decimal_places=2, max_digits=100, blank=True, null=True)
    timestamp = models.DateTimeField(default=timezone.now)

Here is the views.py

class ItemListView(ListView):
    model = Item
    paginate_by = 12
    template_name = "store/product_list.html"
    ordering = ['-timestamp']

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        context["qs_json"] = json.dumps(list(Item.objects.values()),cls=DjangoJSONEncoder)
        return context

Here is the scripts.py

<script>
    const data = '{{qs_json}}'

    const rdata = JSON.parse(data.replace(/&quot;/g, '"'))
    console.log(rdata)

    const input = document.getElementById('search_here')
    console.log(input)

    let filteredArr = []

    input.addEventListener('keyup', (e)=>{
        box.innerHTML = ""
        filteredArr = rdata.filter(store=> store['title'].includes(e.target.value))
        console.log(filteredArr)
        if (filteredArr.length > 0){
            filteredArr.map(store=>{
                box.innerHTML += `<b>${store['title']}</b><br>`
            })
        } else {
            box.innerHTML = "<b>No results found...</b>"
        }
    })
</script>

Here is the template.html

<input id="search_here" class="mb-2 form-control" placeholder="Type to search...">

<!--Card-->
<div id="box" class='row card-group'>
{% for item in object_list %}
  <div class="col-4 mb-3">
    <div class="card h-100">
        <a href="{{item.get_absolute_url}}">
      <embed src="{{ item.image.url }}" class="card-img-top" alt="..."/>
        </a>
      <div class="card-body">
        <h5 class="card-title">{{ item.title }}</h5>
          <p class="card-text">
        {% if item.description %}
            {{ item.description }}
        {% endif %}
        </p>
      </div>
      <div class="card-footer">
        <small class="text-muted">{{ item.timestamp }}</small>
      </div>
    </div>
  </div>
{% endfor %}
</div>
<!--Card-->

My Question: How do I replace the simple <b>${store['title']}</b><br> with the below in the scripts with the whole card div with all its information related to it?

If any more information or clarifications required let me know

  <div class="col-4 mb-3">
    <div class="card h-100">
        <a href="{{item.get_absolute_url}}">
      <embed src="{{ item.image.url }}" class="card-img-top" alt="..."/>
        </a>
      <div class="card-body">
        <h5 class="card-title">{{ item.title }}</h5>
          <p class="card-text">
        {% if item.description %}
            {{ item.description }}
        {% endif %}
        </p>
      </div>
      <div class="card-footer">
        <small class="text-muted">{{ item.timestamp }}</small>
      </div>
    </div>
  </div>

Advertisement

Answer

Your template.html page already have all htmls generated using for-loop so you don’t need to generate them again .Instead , when user type you can loop through all div and check if card-title div has that input if it has simply show that div or else hide it.

Demo Code :

const input = document.getElementById('search_here')
input.addEventListener('keyup', (e) => {
  var inputs = e.target.value.toLowerCase(); //do lowercase
  //loop through outer div and hide it
  document.querySelectorAll('.outers').forEach(function(el) {
    el.style.display = 'none';
  });
  //loop through outer ->card-title
  document.querySelectorAll('.outers .card-title').forEach(function(el) {
    //compare 
    if (el.textContent.toLowerCase().indexOf(inputs) > -1) {
      el.closest('.outers').style.display = "block"; //if match show that div
    }
  })
})
.outers {
  border: 1px solid blue;
  width: 150px;
  height: 150px;
  margin-bottom: 5px
}
<input id="search_here" class="mb-2 form-control" placeholder="Type to search...">
<div id="box" class='row card-group'>
  <!--just added one extra class-->
  <div class="col-4 mb-3 outers">
    <div class="card h-100">
      <a href="{{item.get_absolute_url}}">
        <img src="{{ item.image.url }}" class="card-img-top" alt="..." />
      </a>
      <div class="card-body">
        <h5 class="card-title">Somethings</h5>
        <p class="card-text">
          some things ...
        </p>
      </div>
      <div class="card-footer">
        <small class="text-muted">12:30:00</small>
      </div>
    </div>
  </div>
  <div class="col-4 mb-3 outers">
    <div class="card h-100">
      <a href="{{item.get_absolute_url}}">
        <img src="{{ item.image.url }}" class="card-img-top" alt="..." />
      </a>
      <div class="card-body">
        <h5 class="card-title">Abc</h5>
        <p class="card-text">
          some things ...
        </p>
      </div>
      <div class="card-footer">
        <small class="text-muted">12:30:00</small>
      </div>
    </div>
  </div>
  <div class="col-4 mb-3 outers">
    <div class="card h-100">
      <a href="{{item.get_absolute_url}}">
        <img src="{{ item.image.url }}" class="card-img-top" alt="..." />
      </a>
      <div class="card-body">
        <h5 class="card-title">def</h5>
        <p class="card-text">
          some things ...
        </p>
      </div>
      <div class="card-footer">
        <small class="text-muted">12:30:00</small>
      </div>
    </div>
  </div>
</div>
User contributions licensed under: CC BY-SA
2 People found this is helpful
Advertisement