Django app: how to read a bit of html in javascript
I have a django app that has an design issue I'd like to fix: a script wants to inject a piece of html that contain django tags.
What we have now works, but has some unfortunate aspects:
- Save the script as an html snippet that is nothing but a script: the first line is
<script>
and the last line closes that script. - Pages which use this script import it just like any html snippet.
- This script sets a local variable to the html bit, as a long string, then finds where the html bit is wanted, and calls insertAdjacentHTML to insert it.
So the snippet is an html file that contains nothing but a script, and that script in turn contains hard-coded html as a string. This is confusing to read and prevents our javascript and html linters from working on this snippet. But the code works, and the script can be used by any page by simply importing the one snippet.
Is there a cleaner solution? My ideal solution would:
- Store the html bit in an html file.
- Store the script in a .js file.
- Allow pages that use the script to just load the script.
I have tried the following, none of which work, and none which meet all of these goals:
1: Make the html snippet into a static javascript file, while retaining the hard-coded html inside. Load this as any static script.
Unfortunately this fails, because the django tags are not replaced. It also still has hard-coded html in a javascript file.
2: Save the html as a file and read it using an fs call. Apparently fs calls aren't available in our environment and I'm not sure it's worth adding the necessary extra library.
3: Use the domain model to get at the html bit, as follows:
- Move the html bit to a new html snippet, putting it in a new hidden div with a unique id.
- Have the page that loads the script include the html snippet first, at the end of the
{% block content %}
section and also have it include the html script snippet (if I could get this working, I'd try moving it to a static js file, but one change at a time). - Have the script use the domain model to get the html bit's containing div, get the html as a string using
innerHTML
, then delete the hidden div (to prevent the hidden html bit from messing up the the application).
This fails in unit tests in a way that shows that the new html bit is not deleted. But I will say that when I run the application locally it seems to work, and the django tags are being handled correctly. Also if I remove the "hidden" I don't see the html bit when interacting wth the server. So I'm not really sure what's going on. But I think that the broken tests show this solution is not fully correct.
In any case, this solution has its own issues:
- It is risky to add unwanted html to a page (as the broken tests show).
- It requires any page loading the script to also import the relevant html snippet.
I could change the hidden html so that it could not intefere (i.e. pick new names and then edit them in the script) but that seems ugly and error-prone.
Any ideas for a cleaner solution? I tried lots of web searching, but suspect I am not asking the question right.
To address the design issue in your Django app, where you want to inject HTML containing Django template tags using a JavaScript script, you can follow a cleaner approach. The goal is to separate the HTML and JavaScript into their respective files while ensuring that Django template tags are correctly processed. Here's a step-by-step solution:
Step 1: Store the HTML in a Django Template Create a Django Template: Store the HTML snippet in a separate Django template file. This allows Django to process the template tags.
<!-- templates/snippets/my_snippet.html -->
<div id="my-snippet">
<!-- Your HTML with Django template tags -->
<p>{{ some_variable }}</p>
</div>
Step 2: Create a JavaScript File Create a JavaScript File: Store your JavaScript logic in a separate .js file. This script will be responsible for injecting the HTML into the desired location.
// static/js/inject_snippet.js
document.addEventListener('DOMContentLoaded', function() {
// Fetch the HTML snippet from the server
fetch('/get-snippet/')
.then(response => response.text())
.then(html => {
// Find the target element where you want to inject the HTML
const targetElement = document.getElementById('target-element-id');
if (targetElement) {
// Insert the HTML snippet
targetElement.insertAdjacentHTML('beforeend', html);
}
})
.catch(error => console.error('Error fetching the snippet:', error));
});
Step 3: Create a Django View to Render the HTML Snippet Create a Django View: Create a view in Django that renders the HTML snippet. This view will process the template tags and return the rendered HTML.
# views.py
from django.shortcuts import render
def get_snippet(request):
# Render the HTML snippet with any context variables
context = {
'some_variable': 'Some Value'
}
return render(request, 'snippets/my_snippet.html', context)
Map the View to a URL: Add a URL pattern to map the view to a specific URL.
# urls.py
from django.urls import path
from .views import get_snippet
urlpatterns = [
path('get-snippet/', get_snippet, name='get_snippet'),
]
Step 4: Include the JavaScript in Your Template Include the JavaScript in Your Template: Include the JavaScript file in your main template or the specific template where you need the snippet.
<!-- templates/base.html or specific template -->
<!DOCTYPE html>
<html lang="en">
<head>
<!-- Other head elements -->
<script src="{% static 'js/inject_snippet.js' %}" defer></script>
</head>
<body>
<!-- Your content -->
<div id="target-element-id">
<!-- The HTML snippet will be injected here -->
</div>
</body>
</html>
Explanation Separation of Concerns: By separating the HTML and JavaScript into their respective files, you maintain a clean structure and make the code easier to read and maintain. Django Template Processing: The HTML snippet is processed by Django, ensuring that all template tags are correctly rendered. Dynamic Injection: The JavaScript fetches the rendered HTML snippet from the server and injects it into the desired location in the DOM. Reusability: The JavaScript file can be included in any template, making the solution reusable across different pages. This approach ensures that your HTML and JavaScript are cleanly separated, and Django template tags are correctly processed. It also avoids the issues you encountered with hard-coded HTML strings and unwanted HTML elements in your tests.