如何保存对多个 contenteditable 元素的更改?
How to save changes on multiple contenteditable elements?
我想让我的照片标题可编辑,然后将更改保存在本地存储中。它适用于第一张照片,但当我想编辑另一张照片时,更改将保存到第一张照片而不是我编辑的照片。
这是我的代码的一部分,我在其中显示带标题的照片:
<div class="gallery">
<a href="<?=$path1; ?>">
<img src="<?= $path1; ?>">
</a>
<div id="contenteditable">
<p id="caption-photo" contenteditable> <?=pathinfo($path1, PATHINFO_FILENAME)?></p>
</div>
</div>
保存修改的是我的js代码:
const editables = document.querySelectorAll("[contenteditable]");
editables.forEach(el => {
el.addEventListener("blur", () => {
localStorage.setItem("dataStorage-" + el.id, el.innerHTML);
})
});
for (var key in localStorage) {
if (key.includes("dataStorage-")) {
const id = key.replace("dataStorage-","");
document.querySelector("#" + id).innerHTML = localStorage.getItem(key);
}
}
这里有一个建议:假设您的 foreach
循环是:
<?=
#Other php stuff here
#You can define a counter variable:
$counter = 1;
?>
<div class="gallery">
<?= foreach($some_records as $your_value){ ?>
<a href="<?=$path1; ?>">
<img src="<?= $path1; ?>">
</a>
<div id="contenteditable">
#Which you can append to the caption-photo
#So the id of the first will be: caption-photo1
<p id="caption-photo<?= $counter ?>" contenteditable> <?=pathinfo($path1, PATHINFO_FILENAME)?></p>
<?=
$counter++;
#At the end of each loop, increase the value of counter by 1
}
?>
</div>
也不要介意 foreach
循环,我确定它与你的不匹配,但只需在 id 上附加一个数字的方面,这样你就可以获得不同的值
好吧,因为那行不通,这里是上面的一个片段,但没有使用 short_open_tags
(<?= ... =>
),而是我只使用了 php
(<?php ... ?>
) 标签原样:
首先尝试 运行 代码并检查日志
<?php
$some_records = ["record1.jpg", "record2.jpg", "record3.jpg", "record4.jpg", "record5.jpg"];
$counter = 1;
?>
<div class="gallery">
<?php foreach ($some_records as $path) {
echo '<a href="' . $path . '">
<img src="' . $path . '">
</a>
<div id="contenteditable">
<p id="caption-photo' . $counter . '" contenteditable>' . pathinfo($path, PATHINFO_FILENAME) . '</p>
</div>';
$counter++;
}
?>
</div>
<script>
document.addEventListener("DOMContentLoaded", function(){
const editables = document.querySelectorAll("[contenteditable]");
editables.forEach(el => {
el.addEventListener("blur", () => {
localStorage.setItem("dataStorage-" + el.id, el.innerHTML);
console.log(el.innerHTML);
//When you change the value, it is logged
})
});
/* The loop below does not work as expected */
// for (var key in localStorage) {
// if (key.includes("dataStorage-")) {
// const id = key.replace("dataStorage-", "");
// document.querySelector("#" + id).innerHTML = localStorage.getItem(key);
// ---- The above direct way of selecting the element does not work
// console.log(localStorage.getItem(key));
// }
// }
/* 1 is added to editables.length because the counter starts
from 1 and not 0 */
for(var i = 1; i < editables.length+1; i++){
console.log("dataStorage-caption-photo" + i + " => " + localStorage.getItem("dataStorage-caption-photo" + i));
//This logs all the values associated with the elements
}
});
</script>
以上代码中的元素是这样设置的:
如您所见,计数器工作正常。
并且根据注释掉的循环,这部分 returns 是一个错误,因为 "direct" 访问元素的方法不起作用:
这里有一些代码可以帮助您:
将这个放在 php
文件
中的所有内容之前
if(isset($_GET["old_name"]) && isset($_GET["new_name"])){
$old_name = $_GET["old_name"];
// The same variables in the `GET` request
$new_name = $_GET["new_name"];
// Also remember the extension of the files
$ext = ".jpeg";
// As for rename you have to give the full path
// If your folder is uploads/, and the file is oldname.jpg
// Then it should be rename("uploads/" . $old_name $ext, "uploads/" . $new_name . $ext);
// Just provide the full path to the file you are renaming
// If the images are within the same directory, its ok as it is
echo rename($old_name . $ext, $new_name . $ext);
// returns true if successful
// returns false if not successful
// This is the "responseText" that Javascript will be logging
}
并在 <script>
标签的开头添加此功能:
function update_file(old_name, new_name) {
var update = new XMLHttpRequest();
// Initialize the request
update.open("GET", "?old_name=" + old_name + "&new_name=" + new_name, true);
// Put the variables in the request which will be captured by php
update.onreadystatechange = function() {
if (this.readyState == this.DONE && this.status == 200) {
if (this.responseText != null) {
if(this.responseText){
console.log("Renamed successfully!");
}
else {
console.log("Error renaming!");
}
}
else {
console.log("Not sent!!");
}
}
};
update.send();
}
然后把这段代码改成这样:
const editables = document.querySelectorAll("[contenteditable]");
editables.forEach(el => {
var curr = el.innerHTML;
// Get the current value before it changes
el.addEventListener("blur", () => {
localStorage.setItem("dataStorage-" + el.id, el.innerHTML);
console.log(el.innerHTML);
update_file(curr, el.innerHTML);
// Call the XMLHttpRequest with the values
})
});
这应该让您抢先了解如何使用 XHR 请求(如果您不使用它们的话)...这只是因为您要从 javascript
到 [=13] 获取数据=] 它必须通过 GET
或 POST
...但是从 php
到 javascript
的数据就像将 php
标签和里面的值一样简单
此外,到处都有字面上的评论,因此您可以阅读并理解它的工作原理和原因。不要犹豫,寻求帮助。
------------
好的,在你处理这个的时候,这里有一个提示——在 foreach
循环中,你可以像这样回显一个隐藏的输入:... <input type="hidden" name="directory-' . $counter . ' " value=" ' . pathinfo($path1, PATHINFO_DIRNAME) . ' "/>
(假设这段代码在 foreach
loop) ...以及它下面的相同隐藏输入用于扩展名,因为图像可能有不同的扩展名 --(PATHINFO_EXTENSION) 那么你有 "directory-(number)" 而不是 "directory-(number)" ] ...然后更新发送扩展名和目录的请求 - [update_info(old_name, new_name, extension, directory)] ...意味着您还更新了XHR 请求也发送那些...在 php 端,您只需将目录与文件名和收到的扩展名连接起来。
现在,当谈到从隐藏输入中实际获取值时,请记住有一个计数器,因此根据以特定数字结尾的 el.id
的值,您可以获取名称以相同数字结尾的隐藏输入(这在 forEach 循环中)例如对于扩展的值,取其 name 属性以 "extension-" 开头的值和以与 el.id 相同的值结尾 ... 对目录
也执行此操作
另一种选择是将 el.nextElementSibling.value
作为第一个隐藏输入,然后是下一个隐藏输入,这些将是目录和扩展名的值...
我实际上可以将它们全部写在代码中,但这对您来说不是一个学习过程,编码愉快:) ...希望您能理解上述解释中的概念
好吧,让我打开你的思路......也请原谅我的许多答案,我只是喜欢全面的答案:
创建一个名为 update_file.php
的新 php
文件并将其放在同一目录中。然后把处理请求的php
代码放到这个文件中。
为什么这很重要? 因为我们依赖于从 php 文件收到的 responseText,它会告诉我们请求是否成功。并且,当处理请求的 php 与 html
在同一个文件中时,请求将 return 包含页面 html
内容的响应 ---你可以自己试验一下,看看如果你把下面的代码放在同一个文件中,responseText
将是页面中的全部代码,这不是我们想要的,我们想要 0 或 1 ...好的,够了 说:
把它放在 update_file.php
与当前文件相同的目录中:
<?php
if(isset($_GET["old_name"]) && isset($_GET["new_name"]) && isset($_GET["directory"]) && isset($_GET["extension"])){
$old_name = $_GET["old_name"];
$new_name = $_GET["new_name"];
// The new values from the GET request
$directory = $_GET["directory"];
$extension = $_GET["extension"];
// echo $directory;
// echo $extension;
// Concatenate the values with the directories
// 1 thing to note, if the image is in the current directory,
// the value you will receive for $directory will be "."
// So the method below ensures flexibility with different directories
$dot = ".";
// $dot is added before the extension
$slash = "/";
// $slash is added after the directory name
// if ".", it will be "./' -- for current directory
// if "some/directory" , it will be "some/directory/"
// Then add to the file name and the extension
$full_oldname = $directory . $slash . $old_name . $dot . $extension;
$full_newname = $directory . $slash . $new_name . $dot . $extension;
echo rename($full_oldname, $full_newname);
}
?>
那么这是您当前的文件:
<?php
$some_records = ["record1.jpeg", "uploads/record2.jpeg", "images/subdirectory1/record3.jpeg", "images/record4.jpg", "images/subdirectory2/record5.jpg"];
// Assuming your directory has subdirectories
$counter = 1;
?>
<div class="gallery">
<?php foreach ($some_records as $path) {
echo '<a href="' . $path . '">
<img src="' . $path . '">
</a>
<div id="contenteditable">
<p id="caption-photo' . $counter . '" contenteditable>' . pathinfo($path, PATHINFO_FILENAME) . '</p>
<input type="hidden" name="directory-"' . $counter . '" value="' . pathinfo($path, PATHINFO_DIRNAME) . '" />
<input type="hidden" name="extension-"' . $counter . '" value="' . pathinfo($path, PATHINFO_EXTENSION) . '" />
</div>';
// Note the 2 hidden inputs above
$counter++;
}
?>
</div>
<script>
function update_file(old_name, new_name, directory, extension) {
// The function has been updated with the new parameters
var update = new XMLHttpRequest();
update.open("GET", "update_file.php?old_name=" + old_name + "&new_name=" + new_name + "&directory=" + directory + "&extension=" + extension, true);
// The XHR request has also been updated with the new values
update.onreadystatechange = function() {
if (this.readyState == this.DONE && this.status == 200) {
if (this.responseText != null) {
console.log(this.responseText);
// This will either be 0 or 1, if correct values have been passed to it
if(this.responseText == 1){
console.log("Renamed successfully!");
}
else {
console.log("Error renaming!");
}
}
else {
console.log("Not sent!!");
}
}
};
update.send();
}
document.addEventListener("DOMContentLoaded", function(){
const editables = document.querySelectorAll("[contenteditable]");
editables.forEach(el => {
var curr = el.innerHTML;
// The next element after "el" is the first hidden input with the directory
var dir = el.nextElementSibling.value;
// The next element after the above hidden input is the second hidden input with the extension
// Note the 2 ".nextElementSibling" accessors
var ext = el.nextElementSibling.nextElementSibling.value;
// To get the value of any input(that can lie within a form control) use "[element].value'
console.log(ext);
console.log(dir);
el.addEventListener("blur", () => {
localStorage.setItem("dataStorage-" + el.id, el.innerHTML);
console.log(el.innerHTML);
update_file(curr, el.innerHTML, dir, ext);
})
});
for(var i = 1; i < editables.length+1; i++){
console.log("dataStorage-caption-photo" + i + " => " + localStorage.getItem("dataStorage-caption-photo" + i));
}
});
</script>
也看了评论,这里的重点是:
- 如何添加具有相应值的隐藏输入元素
- 如何使用 javascript
访问值
- 如何将它们添加到 XHR 请求中,然后使用 php
捕获它们
- 最后,如何连接它们并获得完整路径
希望对您有所帮助
这是您可以使用的一些开发人员工具,我不确定您使用的是哪种浏览器,但您可能会发现类似的东西,转到控制台选项卡,然后单击设置并启用 [=19 的日志记录=]
在您移除对任何 contenteditable 元素的关注并确认它们是否正在发送后,您将能够立即看到请求
我想让我的照片标题可编辑,然后将更改保存在本地存储中。它适用于第一张照片,但当我想编辑另一张照片时,更改将保存到第一张照片而不是我编辑的照片。 这是我的代码的一部分,我在其中显示带标题的照片:
<div class="gallery">
<a href="<?=$path1; ?>">
<img src="<?= $path1; ?>">
</a>
<div id="contenteditable">
<p id="caption-photo" contenteditable> <?=pathinfo($path1, PATHINFO_FILENAME)?></p>
</div>
</div>
保存修改的是我的js代码:
const editables = document.querySelectorAll("[contenteditable]");
editables.forEach(el => {
el.addEventListener("blur", () => {
localStorage.setItem("dataStorage-" + el.id, el.innerHTML);
})
});
for (var key in localStorage) {
if (key.includes("dataStorage-")) {
const id = key.replace("dataStorage-","");
document.querySelector("#" + id).innerHTML = localStorage.getItem(key);
}
}
这里有一个建议:假设您的 foreach
循环是:
<?=
#Other php stuff here
#You can define a counter variable:
$counter = 1;
?>
<div class="gallery">
<?= foreach($some_records as $your_value){ ?>
<a href="<?=$path1; ?>">
<img src="<?= $path1; ?>">
</a>
<div id="contenteditable">
#Which you can append to the caption-photo
#So the id of the first will be: caption-photo1
<p id="caption-photo<?= $counter ?>" contenteditable> <?=pathinfo($path1, PATHINFO_FILENAME)?></p>
<?=
$counter++;
#At the end of each loop, increase the value of counter by 1
}
?>
</div>
也不要介意 foreach
循环,我确定它与你的不匹配,但只需在 id 上附加一个数字的方面,这样你就可以获得不同的值
好吧,因为那行不通,这里是上面的一个片段,但没有使用 short_open_tags
(<?= ... =>
),而是我只使用了 php
(<?php ... ?>
) 标签原样:
首先尝试 运行 代码并检查日志
<?php
$some_records = ["record1.jpg", "record2.jpg", "record3.jpg", "record4.jpg", "record5.jpg"];
$counter = 1;
?>
<div class="gallery">
<?php foreach ($some_records as $path) {
echo '<a href="' . $path . '">
<img src="' . $path . '">
</a>
<div id="contenteditable">
<p id="caption-photo' . $counter . '" contenteditable>' . pathinfo($path, PATHINFO_FILENAME) . '</p>
</div>';
$counter++;
}
?>
</div>
<script>
document.addEventListener("DOMContentLoaded", function(){
const editables = document.querySelectorAll("[contenteditable]");
editables.forEach(el => {
el.addEventListener("blur", () => {
localStorage.setItem("dataStorage-" + el.id, el.innerHTML);
console.log(el.innerHTML);
//When you change the value, it is logged
})
});
/* The loop below does not work as expected */
// for (var key in localStorage) {
// if (key.includes("dataStorage-")) {
// const id = key.replace("dataStorage-", "");
// document.querySelector("#" + id).innerHTML = localStorage.getItem(key);
// ---- The above direct way of selecting the element does not work
// console.log(localStorage.getItem(key));
// }
// }
/* 1 is added to editables.length because the counter starts
from 1 and not 0 */
for(var i = 1; i < editables.length+1; i++){
console.log("dataStorage-caption-photo" + i + " => " + localStorage.getItem("dataStorage-caption-photo" + i));
//This logs all the values associated with the elements
}
});
</script>
以上代码中的元素是这样设置的:
如您所见,计数器工作正常。
并且根据注释掉的循环,这部分 returns 是一个错误,因为 "direct" 访问元素的方法不起作用:
这里有一些代码可以帮助您:
将这个放在 php
文件
if(isset($_GET["old_name"]) && isset($_GET["new_name"])){
$old_name = $_GET["old_name"];
// The same variables in the `GET` request
$new_name = $_GET["new_name"];
// Also remember the extension of the files
$ext = ".jpeg";
// As for rename you have to give the full path
// If your folder is uploads/, and the file is oldname.jpg
// Then it should be rename("uploads/" . $old_name $ext, "uploads/" . $new_name . $ext);
// Just provide the full path to the file you are renaming
// If the images are within the same directory, its ok as it is
echo rename($old_name . $ext, $new_name . $ext);
// returns true if successful
// returns false if not successful
// This is the "responseText" that Javascript will be logging
}
并在 <script>
标签的开头添加此功能:
function update_file(old_name, new_name) {
var update = new XMLHttpRequest();
// Initialize the request
update.open("GET", "?old_name=" + old_name + "&new_name=" + new_name, true);
// Put the variables in the request which will be captured by php
update.onreadystatechange = function() {
if (this.readyState == this.DONE && this.status == 200) {
if (this.responseText != null) {
if(this.responseText){
console.log("Renamed successfully!");
}
else {
console.log("Error renaming!");
}
}
else {
console.log("Not sent!!");
}
}
};
update.send();
}
然后把这段代码改成这样:
const editables = document.querySelectorAll("[contenteditable]");
editables.forEach(el => {
var curr = el.innerHTML;
// Get the current value before it changes
el.addEventListener("blur", () => {
localStorage.setItem("dataStorage-" + el.id, el.innerHTML);
console.log(el.innerHTML);
update_file(curr, el.innerHTML);
// Call the XMLHttpRequest with the values
})
});
这应该让您抢先了解如何使用 XHR 请求(如果您不使用它们的话)...这只是因为您要从 javascript
到 [=13] 获取数据=] 它必须通过 GET
或 POST
...但是从 php
到 javascript
的数据就像将 php
标签和里面的值一样简单
此外,到处都有字面上的评论,因此您可以阅读并理解它的工作原理和原因。不要犹豫,寻求帮助。
------------
好的,在你处理这个的时候,这里有一个提示——在 foreach
循环中,你可以像这样回显一个隐藏的输入:... <input type="hidden" name="directory-' . $counter . ' " value=" ' . pathinfo($path1, PATHINFO_DIRNAME) . ' "/>
(假设这段代码在 foreach
loop) ...以及它下面的相同隐藏输入用于扩展名,因为图像可能有不同的扩展名 --(PATHINFO_EXTENSION) 那么你有 "directory-(number)" 而不是 "directory-(number)" ] ...然后更新发送扩展名和目录的请求 - [update_info(old_name, new_name, extension, directory)] ...意味着您还更新了XHR 请求也发送那些...在 php 端,您只需将目录与文件名和收到的扩展名连接起来。
现在,当谈到从隐藏输入中实际获取值时,请记住有一个计数器,因此根据以特定数字结尾的 el.id
的值,您可以获取名称以相同数字结尾的隐藏输入(这在 forEach 循环中)例如对于扩展的值,取其 name 属性以 "extension-" 开头的值和以与 el.id 相同的值结尾 ... 对目录
另一种选择是将 el.nextElementSibling.value
作为第一个隐藏输入,然后是下一个隐藏输入,这些将是目录和扩展名的值...
我实际上可以将它们全部写在代码中,但这对您来说不是一个学习过程,编码愉快:) ...希望您能理解上述解释中的概念
好吧,让我打开你的思路......也请原谅我的许多答案,我只是喜欢全面的答案:
创建一个名为 update_file.php
的新 php
文件并将其放在同一目录中。然后把处理请求的php
代码放到这个文件中。
为什么这很重要? 因为我们依赖于从 php 文件收到的 responseText,它会告诉我们请求是否成功。并且,当处理请求的 php 与 html
在同一个文件中时,请求将 return 包含页面 html
内容的响应 ---你可以自己试验一下,看看如果你把下面的代码放在同一个文件中,responseText
将是页面中的全部代码,这不是我们想要的,我们想要 0 或 1 ...好的,够了 说:
把它放在 update_file.php
与当前文件相同的目录中:
<?php
if(isset($_GET["old_name"]) && isset($_GET["new_name"]) && isset($_GET["directory"]) && isset($_GET["extension"])){
$old_name = $_GET["old_name"];
$new_name = $_GET["new_name"];
// The new values from the GET request
$directory = $_GET["directory"];
$extension = $_GET["extension"];
// echo $directory;
// echo $extension;
// Concatenate the values with the directories
// 1 thing to note, if the image is in the current directory,
// the value you will receive for $directory will be "."
// So the method below ensures flexibility with different directories
$dot = ".";
// $dot is added before the extension
$slash = "/";
// $slash is added after the directory name
// if ".", it will be "./' -- for current directory
// if "some/directory" , it will be "some/directory/"
// Then add to the file name and the extension
$full_oldname = $directory . $slash . $old_name . $dot . $extension;
$full_newname = $directory . $slash . $new_name . $dot . $extension;
echo rename($full_oldname, $full_newname);
}
?>
那么这是您当前的文件:
<?php
$some_records = ["record1.jpeg", "uploads/record2.jpeg", "images/subdirectory1/record3.jpeg", "images/record4.jpg", "images/subdirectory2/record5.jpg"];
// Assuming your directory has subdirectories
$counter = 1;
?>
<div class="gallery">
<?php foreach ($some_records as $path) {
echo '<a href="' . $path . '">
<img src="' . $path . '">
</a>
<div id="contenteditable">
<p id="caption-photo' . $counter . '" contenteditable>' . pathinfo($path, PATHINFO_FILENAME) . '</p>
<input type="hidden" name="directory-"' . $counter . '" value="' . pathinfo($path, PATHINFO_DIRNAME) . '" />
<input type="hidden" name="extension-"' . $counter . '" value="' . pathinfo($path, PATHINFO_EXTENSION) . '" />
</div>';
// Note the 2 hidden inputs above
$counter++;
}
?>
</div>
<script>
function update_file(old_name, new_name, directory, extension) {
// The function has been updated with the new parameters
var update = new XMLHttpRequest();
update.open("GET", "update_file.php?old_name=" + old_name + "&new_name=" + new_name + "&directory=" + directory + "&extension=" + extension, true);
// The XHR request has also been updated with the new values
update.onreadystatechange = function() {
if (this.readyState == this.DONE && this.status == 200) {
if (this.responseText != null) {
console.log(this.responseText);
// This will either be 0 or 1, if correct values have been passed to it
if(this.responseText == 1){
console.log("Renamed successfully!");
}
else {
console.log("Error renaming!");
}
}
else {
console.log("Not sent!!");
}
}
};
update.send();
}
document.addEventListener("DOMContentLoaded", function(){
const editables = document.querySelectorAll("[contenteditable]");
editables.forEach(el => {
var curr = el.innerHTML;
// The next element after "el" is the first hidden input with the directory
var dir = el.nextElementSibling.value;
// The next element after the above hidden input is the second hidden input with the extension
// Note the 2 ".nextElementSibling" accessors
var ext = el.nextElementSibling.nextElementSibling.value;
// To get the value of any input(that can lie within a form control) use "[element].value'
console.log(ext);
console.log(dir);
el.addEventListener("blur", () => {
localStorage.setItem("dataStorage-" + el.id, el.innerHTML);
console.log(el.innerHTML);
update_file(curr, el.innerHTML, dir, ext);
})
});
for(var i = 1; i < editables.length+1; i++){
console.log("dataStorage-caption-photo" + i + " => " + localStorage.getItem("dataStorage-caption-photo" + i));
}
});
</script>
也看了评论,这里的重点是:
- 如何添加具有相应值的隐藏输入元素
- 如何使用 javascript 访问值
- 如何将它们添加到 XHR 请求中,然后使用 php 捕获它们
- 最后,如何连接它们并获得完整路径
希望对您有所帮助
这是您可以使用的一些开发人员工具,我不确定您使用的是哪种浏览器,但您可能会发现类似的东西,转到控制台选项卡,然后单击设置并启用 [=19 的日志记录=]