首页 > 解决方案 > HTML标记阻止 ajax POST

问题描述

我正在使用 Flask 将视频流式传输到网页。

  1. 视频流自行工作。
  2. 然后,我添加了一个与 python 后端函数接口并获取要显示的数据的模式。
  3. 模态显示从 python 函数返回的数据。
  4. 一旦我第一次激活<img>流式传输视频和模态的 html 标签,视频流会阻止 ajax POST,因此模态无法显示数据。

条件 4) 失败。似乎<img>标签阻止了 ajax POST。

此代码是我几天前提交并回答的先前帖子的延续 https://stackoverflow.com/questions/57067923/returned-python-values-break-html-table-after-inserting-in-javascript-array

这是重现我的错误的完整代码,在此先感谢。

# project/get_data.py: python backend module that return data to be displayed in a modal table

class GetData:
    def __init__(self):
        pass

    def records(self):
        return [(1, 'John Smith', 'Canada'),
                (2, 'Jane Doe', 'United States'),
                (3, 'John Doe', 'Mexico')]
# project/camera.py : camera module that returns encoded frames

import cv2

class VideoCamera(object):
    def __init__(self):
        self.video = cv2.VideoCapture(0)

    def __del__(self):
        self.video.release()

    def get_frame(self):
        ret, frame = self.video.read()
        if ret:
            ret, jpeg = cv2.imencode('.jpg', frame)
            return jpeg.tobytes()
<!-- project/templates/index.html -->
<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="utf-8">
        <style>
body {font-family: Arial, Helvetica, sans-serif;}

/* The Modal (background) */
.modal {
  display: none; /* Hidden by default */
  position: fixed; /* Stay in place */
  z-index: 1; /* Sit on top */
  padding-top: 100px; /* Location of the box */
  left: 0;
  top: 0;
  width: 100%; /* Full width */
  height: 100%; /* Full height */
  overflow: auto; /* Enable scroll if needed */
  background-color: rgb(0,0,0); /* Fallback color */
  background-color: rgba(0,0,0,0.4); /* Black w/ opacity */
}

/* Modal Content */
.modal-content {
  background-color: #fefefe;
  margin: auto;
  padding: 20px;
  border: 1px solid #888;
  width: 80%;
}

/* The Close Button */
.close {
  color: #aaaaaa;
  float: right;
  font-size: 28px;
  font-weight: bold;
}

.close:hover,
.close:focus {
  color: #000;
  text-decoration: none;
  cursor: pointer;
}
</style>
    </head>

<body>
        <!-- Trigger/Open The Modal -->
<button id="myBtn">Open Modal</button>

<!-- The Modal -->
<div id="myModal" class="modal">

  <!-- Modal content -->
  <div class="modal-content">
    <span class="close">&times;</span>
    <div id="dvTable"></div>
  </div>
</div>
<style>
table {
  font-family: arial, sans-serif;
  border-collapse: collapse;
  width: 100%;
}

td, th {
  border: 1px solid #dddddd;
  text-align: left;
  padding: 8px;
}

tr:nth-child(even) {
  background-color: #dddddd;
}

</style>

<br><br>

<!--video feed, uncomment the <img> tag to reproduce error===-->

<!--<img id="vid" src="{{ url_for('video_feed') }}">-->

<!--video feed ==============================================-->

<script src="https://code.jquery.com/jquery-3.4.1.js"
  integrity="sha256-WpOohJOqMqqyKL9FccASB9O0KwACQJpFTUBLTYOVvVU="
  crossorigin="anonymous">
</script>

<script>
// Get the modal
var modal = document.getElementById("myModal");

// Get the button that opens the modal
var btn = document.getElementById("myBtn");

// Get the <span> element that closes the modal
var span = document.getElementsByClassName("close")[0];


// When the user clicks the button, open the modal 
btn.onclick = function() {
  modal.style.display = "block";
  //var Table = document.getElementById("dvTable").innerHTML = "";
  $("#dvTable tr").remove(); 
       $.ajax({
            url: "/_get_data/",
            type: "POST",
            success: function(resp){
                $('div#dvTable').append(resp.data);
            }
        });
}

// When the user clicks on <span> (x), close the modal
span.onclick = function() {
  modal.style.display = "none";

}

// When the user clicks anywhere outside of the modal, close it
window.onclick = function(event) {
  if (event.target == modal) {
    modal.style.display = "none";
  }
}
</script>

</body>
</html>
<!--project/templates/response.html-->

    <table>
            <tr>
                <th>Customer Id</th>
                <th>Name</th>
                <th>Country</th>
            </tr>
            {% for elem in myList %}
            <tr>
                <td>{{elem[0]}}</td>
                <td>{{elem[1]}}</td>
                <td>{{elem[2]}}</td>
            </tr>
            {% endfor %}
    </table>
# project/app.py : flask framework

from flask import Flask, render_template, Response, jsonify
from camera import VideoCamera
from get_data import GetData

app = Flask(__name__)

@app.route('/')
def index():
    return render_template('index.html')

@app.route('/_get_data/', methods=['POST'])
def _get_data():
    data = GetData()
    myList = data.records()
    return jsonify({'data': render_template('response.html', myList=myList)})

def gen(camera):
    """Video streaming generator function."""
    while True:
        frame = camera.get_frame()
        yield (b'--frame\r\n'
               b'Content-Type: image/jpeg\r\n\r\n' + frame + b'\r\n\r\n')

@app.route('/video_feed')
def video_feed():
    """Video streaming route. Put this in the src attribute of an img tag."""
    return Response(gen(VideoCamera()),   
                    mimetype='multipart/x-mixed-replace; boundary=frame')

if __name__ == "__main__":
    app.run(debug=True)

如何重现错误

  1. 中的<img>标签templates/index.html目前已被注释掉:
<!--video feed, uncomment the <img> tag to reproduce error===-->

<!--<img id="vid" src="{{ url_for('video_feed') }}">-->

<!--video feed ==============================================-->
  1. python app.py
  2. 在浏览器中打开 localhost:5000 并点击,open modal应该可以看到get_data.py返回的客户信息处于工作状态。
  3. 停止烧瓶服务器Ctrl+C。取消注释<img>标签templates/index.html并将相机连接到您的 PC 并python app.py第二次运行。点击open modal,获取数据创建模态表失败。

预期结果是在视频流式传输时显示模态内容。目前,显示结果的唯一方法<img>是禁用标签,谢谢。

标签: javascripthtmlajax

解决方案


我找到了解决这个问题的方法:1)更新到 1.1.1 的烧瓶修复了这个问题,没有对代码做任何更改。

2)另一种解决方案是放弃ajax请求并template.html进行以下更改:

<!-- project/templates/index.html -->
<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="utf-8">
        <style>
body {font-family: Arial, Helvetica, sans-serif;}

/* The Modal (background) */
.modal {
  display: none; /* Hidden by default */
  position: fixed; /* Stay in place */
  z-index: 1; /* Sit on top */
  padding-top: 100px; /* Location of the box */
  left: 0;
  top: 0;
  width: 100%; /* Full width */
  height: 100%; /* Full height */
  overflow: auto; /* Enable scroll if needed */
  background-color: rgb(0,0,0); /* Fallback color */
  background-color: rgba(0,0,0,0.4); /* Black w/ opacity */
}

/* Modal Content */
.modal-content {
  background-color: #fefefe;
  margin: auto;
  padding: 20px;
  border: 1px solid #888;
  width: 80%;
}

/* The Close Button */
.close {
  color: #aaaaaa;
  float: right;
  font-size: 28px;
  font-weight: bold;
}

.close:hover,
.close:focus {
  color: #000;
  text-decoration: none;
  cursor: pointer;
}
</style>
    </head>

<body>
  <!-- OPEN THE CONTENT WHEN USER CLICKS MODAL-->
<button id="myBtn" onclick="document.getElementById('myModal').style.display='block'">Open Modal</button>

<!-- The Modal -->
<div id="myModal" class="modal">

  <!-- Modal content -->
  <div class="modal-content">
    <span class="close">&times;</span>
    <div id="dvTable">
      <!-- INSERT THE TABLE IN THE MODAL-->
      <table>
       <table>
        <tr>
          <th>Customer Id</th>
          <th>Name</th>
          <th>Country</th>
        </tr>

        <tr>
        <!-- loop through data -->  
        {% for elem in myList %}
        <tr>
          <td>{{elem[0]}}</td>
          <td>{{elem[1]}}</td>
          <td>{{elem[2]}}</td>
        </tr>
        {%endfor %}
        </tr>

    </table>

    </table>

    </div>
  </div>
</div>
<style>
table {
  font-family: arial, sans-serif;
  border-collapse: collapse;
  width: 100%;
}

td, th {
  border: 1px solid #dddddd;
  text-align: left;
  padding: 8px;
}

tr:nth-child(even) {
  background-color: #dddddd;
}

<br><br>

</style>

<img id="bg" src="{{ url_for('video_feed') }}">

<!--<script src="https://code.jquery.com/jquery-3.4.1.js"
  integrity="sha256-WpOohJOqMqqyKL9FccASB9O0KwACQJpFTUBLTYOVvVU="
  crossorigin="anonymous">
</script>-->


<script>
// Get the modal
var modal = document.getElementById("myModal");

// Get the button that opens the modal
var btn = document.getElementById("myBtn");

// Get the <span> element that closes the modal
var span = document.getElementsByClassName("close")[0];

//AJAX POST COMMENTED OUT
/* 
// When the user clicks the button, open the modal 
btn.onclick = function() {
  modal.style.display = "block";
  //var Table = document.getElementById("dvTable").innerHTML = "";
  $("#dvTable tr").remove(); 
       $.ajax({
            url: "/_get_data/",
            type: "POST",
            success: function(resp){
                $('div#dvTable').append(resp.data);
            }
        });
}
*/
// When the user clicks on <span> (x), close the modal
span.onclick = function() {
  modal.style.display = "none";

}

// When the user clicks anywhere outside of the modal, close it
window.onclick = function(event) {
  if (event.target == modal) {
    modal.style.display = "none";
  }
}
</script>

</body>
</html>
# project/app.py

from flask import Flask, render_template, Response, jsonify
from camera import VideoCamera
from get_data import GetData

app = Flask(__name__)



@app.route('/')
def index():
    # get the data and render it to the index page with a variable
    data = GetData()
    myList = data.records()
    return render_template('index.html', myList=myList)

""" NO LONGER NEEDED TO SERVE DATA TO AJAX POST
@app.route('/_get_data/', methods=['POST'])
def _get_data():
    data = GetData()
    myList = data.records()
    return jsonify({'data': render_template('response.html', myList=myList)})
"""
def gen(camera):
    """Video streaming generator function."""
    while True:
        frame = camera.get_frame()
        yield (b'--frame\r\n'
               b'Content-Type: image/jpeg\r\n\r\n' + frame + b'\r\n\r\n')

@app.route('/video_feed')
def video_feed():
    """Video streaming route. Put this in the src attribute of an img tag."""
    return Response(gen(VideoCamera()),   
                    mimetype='multipart/x-mixed-replace; boundary=frame')

if __name__ == "__main__":
    app.run(debug=True)

希望这对其他人有帮助。


推荐阅读