如何动态创建 javascript 表单元素
How to create javascript form elements dynamically
我有一个 vuejs 页面,我正在动态创建 DOM 输入,我想将它们与一组 3 个表单变量绑定以捕获数据。
我不知道他们会从一开始就创建多少组输入来初始化表单条目。
表单是在 Vue 的 setup() 部分使用 useForm 创建的。
以下是代码片段。
<template>
<app-layout>
<template #header>
<h2 class="font-semibold text-xl text-gray-800 leading-tight">
{{ title }}
</h2>
</template>
<div class="py-12">
<div class="max-w-7xl mx-auto sm:px-6 lg:px-8">
<div class="bg-white overflow-hidden shadow-xl sm:rounded-lg">
<div class="audio_edit">
<form @submit.prevent="form.post('/pppaudio/newProcessedAudio')">
<div class="pp_title">
<label style="font-weight: bold">
Title:
</label>
<label for="title">{{ title }}</label>
</div>
<div class="pp_audio">
<div id="waveform"></div>
<div id="wave-timeline"></div>
<div class="playButton">
<Button data-action="play" type="button">
<span id="play">
<i class="glyphicon glyphicon-play"></i>
Play
</span>
<span id="pause" style="display: none">
<i class="glyphicon glyphicon-pause"></i>
Pause
</span>
</Button>
</div>
</div>
<div class="pp_transcript flex flex-col">
<label style="font-weight: bold">
Transcript:
</label>
{{ transcript }}
</div>
<div id="region_block" ref="reg_block" >
<div class="region">
<div id="copyTextButton" class="copyButton">
<Button v-on:click="getText" type="button">Copy Marked Text</Button>
</div>
<div class="pp_new_transcript flex flex-col">
<label style="font-weight: bold">
New Transcript:
</label>
<textarea id="selectedText" rows="5" class="selectedTextInput h-full" v-model="form.selectedText" />
</div>
<div class="pp_start-stop">
<label for="start" style="font-weight: bold">
Start:
</label>
<input id="start" v-model="form.start" disabled/>
<label for="stop" style="font-weight: bold">
Stop:
</label>
<input id="stop" v-model="form.stop" disabled/>
</div>
<div id="submit_button">
<Button>Submit</Button>
</div>
</div>
</div>
<div id="return_back">
<Button v-on:click="goBack" type="button">Back</Button>
</div>
</form>
</div>
</div>
</div>
</div>
</app-layout>
</template>
<script>
import AppLayout from "../../Layouts/AppLayout";
import Label from "../../Jetstream/Label";
import Button from "../../Jetstream/Button";
import {InputFacade} from 'vue-input-facade';
import WaveSurfer from 'wavesurfer.js';
import RegionPlugin from 'wavesurfer.js/dist/plugin/wavesurfer.regions.js';
import TimeLine from 'wavesurfer.js/dist/plugin/wavesurfer.timeline.js';
import {useForm} from '@inertiajs/inertia-vue3';
import Vue from 'vue'
export default {
setup() {
const form = useForm({
title: null,
selectedText: null,
start: null,
stop: null,
})
return {form}
},
components: {Button, Label, AppLayout, InputFacade},
methods: {
getText() {
this.newTranscript = window.getSelection().toString();
this.form.selectedText = this.newTranscript;
this.form.title = this.title;
},
copyValues(start, stop) {
this.form.start = start;
this.form.stop = stop;
},
goBack() {
let formData = new FormData();
formData.append('id', localStorage.id);
axios.post('/pppaudio/goBack',
formData, {
headers: {
'Content-Type': 'multipart/form-data'
}
}
).then( (response) => {
if (response.data.status === 'success') {
window.location.href = response.data.url;
}
}).catch(error => {
console.log("ERRRR:: ", error.response.data);
});
}
},
mounted() {
if (localStorage.title) {
this.title = localStorage.title;
}
if (localStorage.path) {
this.path = localStorage.path;
}
if (localStorage.transcript) {
this.transcript = localStorage.transcript;
}
const wavesurfer = WaveSurfer.create({
container: '#waveform',
waveColor: 'violet',
progressColor: 'purple',
scrollParent: true,
plugins: [
RegionPlugin.create({
dragSelection: true
}),
TimeLine.create({
container: '#wave-timeline'
})
]
});
wavesurfer.load('../storage/preprocessed-audio/' + this.path);
let play = false;
wavesurfer.on('region-click', (region, e) => {
e.stopPropagation();
// Play on click, loop on shift click
if (play) {
wavesurfer.pause();
play = false;
} else {
region.play();
play = true;
}
// console.log(this.form.title);
this.copyValues(region.start, region.end);
});
wavesurfer.on('region-created', (region, e) => {
console.log(region.id.substring(10))
let regionNode = document.createElement("div")
regionNode.className = "region"
let regionTitle = document.createElement("div")
regionTitle.className = "regionTitle"
regionTitle.innerHTML = region.id
regionTitle.style.fontWeight = "bold"
regionNode.appendChild(regionTitle)
let color = () => Math.random() * 256 >> 0;
let col = `${color()}, ${color()}, ${color()}`
regionTitle.style.color = 'rgb('+col+')';
region.color = 'rgba('+ col + ', 0.1)';
let copyButtonDiv = document.createElement("div")
copyButtonDiv.className = "copyTextButton"
let copyButton = document.createElement("Button")
copyButton.className = "inline-flex items-center px-4 py-2 bg-gray-800 border border-transparent rounded-md font-semibold text-xs text-white uppercase tracking-widest hover:bg-gray-700 active:bg-gray-900 focus:outline-none focus:border-gray-900 focus:ring focus:ring-gray-300 disabled:opacity-25 transition"
let copyButtonText = document.createTextNode("Copy Marked Text")
copyButton.appendChild(copyButtonText)
copyButton.addEventListener('click', this.getText)
let cpBtnType = document.createAttribute("type")
cpBtnType.value = "button"
copyButton.setAttributeNode(cpBtnType)
copyButtonDiv.appendChild(copyButton)
regionNode.appendChild(copyButtonDiv)
let newTranscriptDiv = document.createElement("div")
newTranscriptDiv.className = "pp_new_transcript flex flex-col"
let newTransLabel = document.createElement("label")
newTransLabel.innerText = "New Transcript:"
newTransLabel.style.fontWeight = "bold"
let selectTextArea = document.createElement("textarea")
selectTextArea.id = "selectedText"
selectTextArea.className = "selectedTextInput h-full"
let selectTextAreType = document.createAttribute("rows")
selectTextAreType.value = "5"
selectTextArea.setAttributeNode(selectTextAreType)
newTranscriptDiv.appendChild(newTransLabel)
newTranscriptDiv.appendChild(selectTextArea)
regionNode.appendChild(newTranscriptDiv)
let startStopDiv = document.createElement("div")
startStopDiv.className = "pp_start-stop"
let startLabel = document.createElement("label")
startLabel.innerText = "Start:"
startLabel.style.fontWeight = "bold"
let startLabelType = document.createAttribute("for")
startLabelType.value = "start"
startLabel.setAttributeNode(startLabelType)
let startInput = document.createElement("input")
startInput.id = "start"
let startInputType = document.createAttribute("disabled")
startInput.setAttributeNode(startInputType)
let stopLabel = document.createElement("label")
stopLabel.innerText = "Stop:"
stopLabel.style.fontWeight = "bold"
let stopLabelType = document.createAttribute("for")
stopLabelType.value = "stop"
stopLabel.setAttributeNode(stopLabelType)
let stopInput = document.createElement("input")
stopInput.id = "stop"
let stopInputType = document.createAttribute("disabled")
stopInput.setAttributeNode(stopInputType)
startStopDiv.appendChild(startLabel)
startStopDiv.appendChild(startInput)
startStopDiv.appendChild(stopLabel)
startStopDiv.appendChild(stopInput)
regionNode.appendChild(startStopDiv)
let submitBtnDiv = document.createElement("div")
submitBtnDiv.id = "submit_button"
let submitButton = document.createElement("Button")
let submitButtonText = document.createTextNode("Submit")
submitButton.className = "inline-flex items-center px-4 py-2 bg-gray-800 border border-transparent rounded-md font-semibold text-xs text-white uppercase tracking-widest hover:bg-gray-700 active:bg-gray-900 focus:outline-none focus:border-gray-900 focus:ring focus:ring-gray-300 disabled:opacity-25 transition"
submitButton.appendChild(submitButtonText)
submitBtnDiv.appendChild(submitButton)
regionNode.appendChild(submitBtnDiv)
// this.$set()
document.querySelector('#region_block').appendChild(regionNode)
})
let playButton = document.querySelector('#play');
let pauseButton = document.querySelector('#pause');
wavesurfer.on('play', function() {
playButton.style.display = 'none';
pauseButton.style.display = '';
});
wavesurfer.on('pause', function() {
playButton.style.display = '';
pauseButton.style.display = 'none';
});
playButton.addEventListener('click', function () {
wavesurfer.play()
})
pauseButton.addEventListener('click', function () {
wavesurfer.pause()
})
},
data() {
return {
title: '',
path: '',
transcript: '',
selectedText: '',
newTranscript: '',
start: '',
stop: ''
}
}
}
</script>
<style>
.audio_edit {
padding: 10px;
}
.pp_title, .pp_audio, .copyButton, .pp_transcript, .pp_new_transcript, .pp_start-stop, #wave-timeline, #submit_button {
padding-bottom: 10px;
}
.region {
border-top: 3px solid black;
border-bottom: 3px solid black;
padding-top: 3px;
margin-bottom: 3px;
}
.pp_new_transcript {
display: flex;
width: 100%;
}
.selectedTextInput {
-webkit-box-flex:1;
-webkit-flex:1;
-ms-flex:1;
flex:1;
border: none;
}
</style>
每当你需要一些 HTML 在 Vue 中重复时,使用 v-for。在下面的示例中,我将它与 component
一起使用
在 Vue 中很少需要使用 createElement
,应该避免使用 。如果您考虑我下面的示例,您应该看到整个 wavesurfer.on('region-created')
部分可以通过将新的 Region 对象推入 v-for
中使用的数组来替换
const app = Vue.createApp({
data() {
return {
forms: [{
id: 1,
selectedText: 'Hello',
start: 0,
stop: 4
},
{
id: 2,
selectedText: 'world',
start: 6,
stop: 10
},
]
}
}
})
app.component('my-form', {
props: {
form: {
type: Object,
required: true
}
},
template: `
<div class="region">
<div id="copyTextButton" class="copyButton">
<button type="button">Copy Marked Text</button>
</div>
<div class="pp_new_transcript flex flex-col">
<label style="font-weight: bold">
New Transcript:
</label>
<textarea id="selectedText" rows="5" class="selectedTextInput h-full" v-model="form.selectedText" />
</div>
<div class="pp_start-stop">
<label for="start" style="font-weight: bold">
Start:
</label>
<input id="start" v-model="form.start" disabled/>
<label for="stop" style="font-weight: bold">
Stop:
</label>
<input id="stop" v-model="form.stop" disabled/>
</div>
<div id="submit_button">
<button>Submit</button>
</div>
</div>
`
})
app.mount('#app')
<script src="https://unpkg.com/vue@3.1.4/dist/vue.global.js"></script>
<div id='app'>
<my-form v-for="form in forms" :key="form.id" :form="form"></my-form>
</div>
我有一个 vuejs 页面,我正在动态创建 DOM 输入,我想将它们与一组 3 个表单变量绑定以捕获数据。 我不知道他们会从一开始就创建多少组输入来初始化表单条目。 表单是在 Vue 的 setup() 部分使用 useForm 创建的。 以下是代码片段。
<template>
<app-layout>
<template #header>
<h2 class="font-semibold text-xl text-gray-800 leading-tight">
{{ title }}
</h2>
</template>
<div class="py-12">
<div class="max-w-7xl mx-auto sm:px-6 lg:px-8">
<div class="bg-white overflow-hidden shadow-xl sm:rounded-lg">
<div class="audio_edit">
<form @submit.prevent="form.post('/pppaudio/newProcessedAudio')">
<div class="pp_title">
<label style="font-weight: bold">
Title:
</label>
<label for="title">{{ title }}</label>
</div>
<div class="pp_audio">
<div id="waveform"></div>
<div id="wave-timeline"></div>
<div class="playButton">
<Button data-action="play" type="button">
<span id="play">
<i class="glyphicon glyphicon-play"></i>
Play
</span>
<span id="pause" style="display: none">
<i class="glyphicon glyphicon-pause"></i>
Pause
</span>
</Button>
</div>
</div>
<div class="pp_transcript flex flex-col">
<label style="font-weight: bold">
Transcript:
</label>
{{ transcript }}
</div>
<div id="region_block" ref="reg_block" >
<div class="region">
<div id="copyTextButton" class="copyButton">
<Button v-on:click="getText" type="button">Copy Marked Text</Button>
</div>
<div class="pp_new_transcript flex flex-col">
<label style="font-weight: bold">
New Transcript:
</label>
<textarea id="selectedText" rows="5" class="selectedTextInput h-full" v-model="form.selectedText" />
</div>
<div class="pp_start-stop">
<label for="start" style="font-weight: bold">
Start:
</label>
<input id="start" v-model="form.start" disabled/>
<label for="stop" style="font-weight: bold">
Stop:
</label>
<input id="stop" v-model="form.stop" disabled/>
</div>
<div id="submit_button">
<Button>Submit</Button>
</div>
</div>
</div>
<div id="return_back">
<Button v-on:click="goBack" type="button">Back</Button>
</div>
</form>
</div>
</div>
</div>
</div>
</app-layout>
</template>
<script>
import AppLayout from "../../Layouts/AppLayout";
import Label from "../../Jetstream/Label";
import Button from "../../Jetstream/Button";
import {InputFacade} from 'vue-input-facade';
import WaveSurfer from 'wavesurfer.js';
import RegionPlugin from 'wavesurfer.js/dist/plugin/wavesurfer.regions.js';
import TimeLine from 'wavesurfer.js/dist/plugin/wavesurfer.timeline.js';
import {useForm} from '@inertiajs/inertia-vue3';
import Vue from 'vue'
export default {
setup() {
const form = useForm({
title: null,
selectedText: null,
start: null,
stop: null,
})
return {form}
},
components: {Button, Label, AppLayout, InputFacade},
methods: {
getText() {
this.newTranscript = window.getSelection().toString();
this.form.selectedText = this.newTranscript;
this.form.title = this.title;
},
copyValues(start, stop) {
this.form.start = start;
this.form.stop = stop;
},
goBack() {
let formData = new FormData();
formData.append('id', localStorage.id);
axios.post('/pppaudio/goBack',
formData, {
headers: {
'Content-Type': 'multipart/form-data'
}
}
).then( (response) => {
if (response.data.status === 'success') {
window.location.href = response.data.url;
}
}).catch(error => {
console.log("ERRRR:: ", error.response.data);
});
}
},
mounted() {
if (localStorage.title) {
this.title = localStorage.title;
}
if (localStorage.path) {
this.path = localStorage.path;
}
if (localStorage.transcript) {
this.transcript = localStorage.transcript;
}
const wavesurfer = WaveSurfer.create({
container: '#waveform',
waveColor: 'violet',
progressColor: 'purple',
scrollParent: true,
plugins: [
RegionPlugin.create({
dragSelection: true
}),
TimeLine.create({
container: '#wave-timeline'
})
]
});
wavesurfer.load('../storage/preprocessed-audio/' + this.path);
let play = false;
wavesurfer.on('region-click', (region, e) => {
e.stopPropagation();
// Play on click, loop on shift click
if (play) {
wavesurfer.pause();
play = false;
} else {
region.play();
play = true;
}
// console.log(this.form.title);
this.copyValues(region.start, region.end);
});
wavesurfer.on('region-created', (region, e) => {
console.log(region.id.substring(10))
let regionNode = document.createElement("div")
regionNode.className = "region"
let regionTitle = document.createElement("div")
regionTitle.className = "regionTitle"
regionTitle.innerHTML = region.id
regionTitle.style.fontWeight = "bold"
regionNode.appendChild(regionTitle)
let color = () => Math.random() * 256 >> 0;
let col = `${color()}, ${color()}, ${color()}`
regionTitle.style.color = 'rgb('+col+')';
region.color = 'rgba('+ col + ', 0.1)';
let copyButtonDiv = document.createElement("div")
copyButtonDiv.className = "copyTextButton"
let copyButton = document.createElement("Button")
copyButton.className = "inline-flex items-center px-4 py-2 bg-gray-800 border border-transparent rounded-md font-semibold text-xs text-white uppercase tracking-widest hover:bg-gray-700 active:bg-gray-900 focus:outline-none focus:border-gray-900 focus:ring focus:ring-gray-300 disabled:opacity-25 transition"
let copyButtonText = document.createTextNode("Copy Marked Text")
copyButton.appendChild(copyButtonText)
copyButton.addEventListener('click', this.getText)
let cpBtnType = document.createAttribute("type")
cpBtnType.value = "button"
copyButton.setAttributeNode(cpBtnType)
copyButtonDiv.appendChild(copyButton)
regionNode.appendChild(copyButtonDiv)
let newTranscriptDiv = document.createElement("div")
newTranscriptDiv.className = "pp_new_transcript flex flex-col"
let newTransLabel = document.createElement("label")
newTransLabel.innerText = "New Transcript:"
newTransLabel.style.fontWeight = "bold"
let selectTextArea = document.createElement("textarea")
selectTextArea.id = "selectedText"
selectTextArea.className = "selectedTextInput h-full"
let selectTextAreType = document.createAttribute("rows")
selectTextAreType.value = "5"
selectTextArea.setAttributeNode(selectTextAreType)
newTranscriptDiv.appendChild(newTransLabel)
newTranscriptDiv.appendChild(selectTextArea)
regionNode.appendChild(newTranscriptDiv)
let startStopDiv = document.createElement("div")
startStopDiv.className = "pp_start-stop"
let startLabel = document.createElement("label")
startLabel.innerText = "Start:"
startLabel.style.fontWeight = "bold"
let startLabelType = document.createAttribute("for")
startLabelType.value = "start"
startLabel.setAttributeNode(startLabelType)
let startInput = document.createElement("input")
startInput.id = "start"
let startInputType = document.createAttribute("disabled")
startInput.setAttributeNode(startInputType)
let stopLabel = document.createElement("label")
stopLabel.innerText = "Stop:"
stopLabel.style.fontWeight = "bold"
let stopLabelType = document.createAttribute("for")
stopLabelType.value = "stop"
stopLabel.setAttributeNode(stopLabelType)
let stopInput = document.createElement("input")
stopInput.id = "stop"
let stopInputType = document.createAttribute("disabled")
stopInput.setAttributeNode(stopInputType)
startStopDiv.appendChild(startLabel)
startStopDiv.appendChild(startInput)
startStopDiv.appendChild(stopLabel)
startStopDiv.appendChild(stopInput)
regionNode.appendChild(startStopDiv)
let submitBtnDiv = document.createElement("div")
submitBtnDiv.id = "submit_button"
let submitButton = document.createElement("Button")
let submitButtonText = document.createTextNode("Submit")
submitButton.className = "inline-flex items-center px-4 py-2 bg-gray-800 border border-transparent rounded-md font-semibold text-xs text-white uppercase tracking-widest hover:bg-gray-700 active:bg-gray-900 focus:outline-none focus:border-gray-900 focus:ring focus:ring-gray-300 disabled:opacity-25 transition"
submitButton.appendChild(submitButtonText)
submitBtnDiv.appendChild(submitButton)
regionNode.appendChild(submitBtnDiv)
// this.$set()
document.querySelector('#region_block').appendChild(regionNode)
})
let playButton = document.querySelector('#play');
let pauseButton = document.querySelector('#pause');
wavesurfer.on('play', function() {
playButton.style.display = 'none';
pauseButton.style.display = '';
});
wavesurfer.on('pause', function() {
playButton.style.display = '';
pauseButton.style.display = 'none';
});
playButton.addEventListener('click', function () {
wavesurfer.play()
})
pauseButton.addEventListener('click', function () {
wavesurfer.pause()
})
},
data() {
return {
title: '',
path: '',
transcript: '',
selectedText: '',
newTranscript: '',
start: '',
stop: ''
}
}
}
</script>
<style>
.audio_edit {
padding: 10px;
}
.pp_title, .pp_audio, .copyButton, .pp_transcript, .pp_new_transcript, .pp_start-stop, #wave-timeline, #submit_button {
padding-bottom: 10px;
}
.region {
border-top: 3px solid black;
border-bottom: 3px solid black;
padding-top: 3px;
margin-bottom: 3px;
}
.pp_new_transcript {
display: flex;
width: 100%;
}
.selectedTextInput {
-webkit-box-flex:1;
-webkit-flex:1;
-ms-flex:1;
flex:1;
border: none;
}
</style>
每当你需要一些 HTML 在 Vue 中重复时,使用 v-for。在下面的示例中,我将它与 component
一起使用在 Vue 中很少需要使用
中使用的数组来替换createElement
,应该避免使用 。如果您考虑我下面的示例,您应该看到整个wavesurfer.on('region-created')
部分可以通过将新的 Region 对象推入v-for
const app = Vue.createApp({
data() {
return {
forms: [{
id: 1,
selectedText: 'Hello',
start: 0,
stop: 4
},
{
id: 2,
selectedText: 'world',
start: 6,
stop: 10
},
]
}
}
})
app.component('my-form', {
props: {
form: {
type: Object,
required: true
}
},
template: `
<div class="region">
<div id="copyTextButton" class="copyButton">
<button type="button">Copy Marked Text</button>
</div>
<div class="pp_new_transcript flex flex-col">
<label style="font-weight: bold">
New Transcript:
</label>
<textarea id="selectedText" rows="5" class="selectedTextInput h-full" v-model="form.selectedText" />
</div>
<div class="pp_start-stop">
<label for="start" style="font-weight: bold">
Start:
</label>
<input id="start" v-model="form.start" disabled/>
<label for="stop" style="font-weight: bold">
Stop:
</label>
<input id="stop" v-model="form.stop" disabled/>
</div>
<div id="submit_button">
<button>Submit</button>
</div>
</div>
`
})
app.mount('#app')
<script src="https://unpkg.com/vue@3.1.4/dist/vue.global.js"></script>
<div id='app'>
<my-form v-for="form in forms" :key="form.id" :form="form"></my-form>
</div>