在单击图像"modify"时激活输入标签



我正在编写一个网站,其中管理员可以访问仪表板页面,在那里他们可以看到用户列表。我想为管理员添加在同一行中更改其他用户角色的能力。

代码如下:

<table>
<caption> Liste des utilisateurs </caption>
<tr class="title">
<th>Date de création</th>
<th>Prénom</th>
<th>Nom d'utilisateur</th>
<th>Mail</th>
<th>Rôle</th>
<th>Id</th>
</tr>
<tr>
<td>0000-00-00 00:00:00</td>
<td></td>
<td>username</td>
<td>*****@mail.fr</td>
<td>admin</td>
<td>22</td>
<img class="information_modify" src="chemin" alt="img_modifier"> 
</tr> 

<tr>
<td>2022-05-08 09:29:53</td>
<td></td>
<td>username</td>
<td>****@mail.fr</td>
<td>user</td>
<td>23</td>
<img class="information_modify" src="chemin" alt="img_modifier"> 
</tr>
</table>

但是我有两个问题:

  1. 我不知道如何改变<td><input>时,有人点击图像;也许用JavaScript?

  2. 即使我做到了,我怎么能限制改变只有那一行,而不是所有的<td>。我在php中使用forEach循环来生成这段代码。因此,我无法真正生成每行个性化的代码。

我需要你的帮助。谢谢你的回答。

这里有几种方法,第一种是明确回答您的问题,并创建一个系统,其中-当单击.information_modify元素时-单元格的文本内容插入<input>元素(第二次单击该.information_modify元素将信息"保存"到<table>,保存到数据库超出了本问题的范围):

// defining a named function to handle the toggling of the editing,
// using Arrow syntax and passing in the Event Object from the use
// of EventTarget.addEventListener():
const editToggle = (evt) => {
// retrieving the element to which the event-handler was bound:
let toggle = evt.currentTarget,
// here we retrieve the closest ancestor <tr> element:
row = toggle.closest('tr'),
// and here we create a new <input> element:
input = document.createElement('input'),
// retrieving a list of cells from the current <tr>, which
// have the class of 'canModify':
editableCells = row.querySelectorAll('.canModify');
// here we toggle the class of 'editInProgress' on the toggle element's
// ancestor <tr> and <tbody> elements in order to allow for styling and
// detection of whether editing is, or is not, in progress:
row.classList.toggle('editInProgress');
row.closest('tbody').classList.toggle('editInProgress');

// here we test to see if the ancestor <tr> element has the class of
// 'editInProgress' in its classList property:
if (row.classList.contains('editInProgress')) {
// if so, we're in an editing sitation:
// here we iterate over the NodeList, using NodeList.prototype.forEach(),
// along with its anonymous Arrow function:
editableCells.forEach(
// 'cell' is a reference to the current <td> of the NodeList of
// <td> elements over which we're iterating:
(cell) => {
// here we clone the created <input> element:
let clone = input.cloneNode(),
// we retrieve the value of the data-list attribute, using the
// HTMLElement.dataset API; though we could instead have  used
// cell.getAttribute('data-list'):
dataList = cell.dataset.list;

// we set the <input> element's type property to be equal
// to the data-type attribute/property:
clone.type = cell.dataset.type;
// setting the value of the <input> to be equal to the
// text-content of the current <td> element, trimming away
// leading or trailing white-space:
clone.value = cell.textContent.trim();

// here we use CSSStyleDeclaration.setProperty() to set the property
// of the custom CSS Property '--width' to be equal to the rounded-
// value of the width of the <td> element; this is because:
// a: I don't like the way that an <input> of default-width makes the
//    dimensions of the <table> jump around, and
// b: trying to use
//      clone.width = `${Math.round(cell.getBoundingClientRect().width)}px`
//   didn't seem to work (the width kept being set to 0 in the HTML, despite
//   retrieving the correct value):
clone.style.setProperty('--width',`${Math.round(cell.getBoundingClientRect().width)}px`);

// if the dataList is a truthy value (hence it's not
// undefined, null or an empty-string):
if (dataList) {
// we use HTMLElement.setAttribute() to set the
// attribute-value (dataList) of the 'list' attribute:
clone.setAttribute('list', dataList);
}

// we remove the current text-content (having cached it,
// and set the <input> element's value):
cell.textContent = '';
// and append the cloned <input> to the <td>:
cell.append(clone);
});
// otherwise, if the <tr> element does not have the class of 'editInProgress':
} else {
// we again iterate over the editableCells, and again in the same manner:
editableCells.forEach(
(cell) => {
// here we retrieve the first/only <input> found within the
// <td> element:
let input = cell.querySelector('input'),
// we retrieve the dataList element from the <input> element's
// list property:
dataList = input.list,
// and recover the current-value of the <input>:
text = input.value.trim();

// if the dataList is truthy (not null, undefined...),
// and the text is not an empty string,
// and an Array formed from the <option> elements of the
// <datalist> doesn't include the current <input> value:
if (dataList && text.length > 0 && ![...dataList.options].map((opt)=>opt.text).includes(text)) {
// we append a new <option> element to the <datalist>, whose
// text, and therefore value, is set to be equal to the current
// <input> value (so a new option, such as 'Dr' or 'Lead Administrator'
// doesn't have to be fully typed out again, should it be required again):
dataList.append(new Option(text));
}

// we remove all contents of the <td>:
cell.innerHTML = '';
// and we append the text, from the <input> element's value:
cell.append(text);
});
}
},
// caching a NodeList of all '.information_modify' elements:
editButtons = document.querySelectorAll('.information_modify');
// iterating over that NodeList, again using NodeList.prototype.forEach():
editButtons.forEach(
// binding the editToggle() function (note the deliberate lack of parentheses) as
// the event-handler for the 'click' event:
(button) => button.addEventListener('click', editToggle)
);
*, ::before, ::after {
box-sizing: border-box;
font-family: Roboto, Montserrat, system-ui;
font-size: 16px;
font-weight: 400;
line-height: 1.4;
margin: 0;
padding: 0;
}
table {
border-collapse: collapse;
margin-block: 1em;
margin-inline: auto;
table-layout: fixed;
width: clamp(auto, 80vw, 900px);
}
th {
font-weight: bold;
}
th, td {
border-block-end: 2px solid #000;
padding-block: 0.25em;
padding-inline: 0.5em;
}

.information_modify {
aspect-ratio: 1;
border: 1px solid rgb(200 200 200 / 0.7);
cursor: pointer;
display: inline-block;
height: 1em;
overflow: hidden;
}
img:empty {
--alpha: 0.5;
--accent-color: rgb(200 200 200 / var(--alpha));
background-image: repeating-linear-gradient(45deg, transparent 0 5px, var(--accent-color) 5px 8px);
}
img:empty:hover {
--alpha: 1;
}
/* highlighting the <td> elements of the currently-selected
<tr> */
tr.editInProgress > td {
background-color: #ffa;
}
/* any <td> elements which are not found within a <tr> with
the class of 'editInProgress' but are found within an
ancestor (in this case <tbody>) element with the class of
'editInProgress': */
.editInProgress tr:not(.editInProgress) td {
/* to give the impression that other <td> elements
are not interactive while editing in progress: */
cursor: not-allowed;
opacity: 0.4;
pointer-events: none;
user-select: none;
}
input {
/* here we set the width of the <input> elements to be either
equal to the CSS property '--width' or the default width
of 5em if the '--width' property is invalid for any reason: */
width: var(--width, 5em);
}
<!-- using datalist elements to hold potential options where it
makes sense to do so:-->
<datalist id="prenom">
<option>Mr</option>
<option>Ms</option>
<option>Mrs</option>
</datalist>
<datalist id="role">
<option>admin</option>
<option>analyst</option>
<option>executive officer</option>
<option>marketing</option>
<option>sales</option>
<option>superuser</option>
<option>user</option>
</datalist>

<table>
<caption> Liste des utilisateurs </caption>
<!-- wrapping the row of <th> elements in a <thead>: -->
<thead>
<tr class="title">
<th>Date de création</th>
<th>Prénom</th>
<th>Nom d'utilisateur</th>
<th>Mail</th>
<th>Rôle</th>
<th>Id</th>
<th></th>
</tr>
</thead>
<!-- wrapping the contents of the <table> in a <tbody>: -->
<tbody>
<!-- the fake details here were created from a JS Fiddle I created
here: https://jsfiddle.net/davidThomas/mv2docp7/ feel free to
play around for your own development needs: -->
<tr>
<td>1994-01-31 12:16</td>
<!-- I added the class of 'canModify' to the elements that it makes sense
to modify (it doesn't make sense - to me - to modify a user's
id or creation-date), but adjust to taste.
I added the custom data-* attributes for use in the JavaScript,
data-type: determines the <input> type, whereas data-list
identifies the <datalist> element to be associated to that
<input>: -->
<td class="canModify" data-type="text" data-list="prenom"></td>
<td class="canModify" data-type="text">Joanne Randall</td>
<td class="canModify" data-type="email">*****@mail.fr</td>
<td class="canModify" data-type="text" data-list="role">user</td>
<td>1664</td>
<!-- an <img>, in a <table>, must be wrapped in either a <th> or
<td> element; so I wrapped said element in a <td>: -->
<td>
<img class="information_modify" src="chemin" alt="img_modifier">
</td>
</tr>
<tr>
<td>1982-02-19 09:12</td>
<td class="canModify" data-type="text" data-list="prenom"></td>
<td class="canModify" data-type="text">Tim Scott</td>
<td class="canModify" data-type="email">*****@mail.fr</td>
<td class="canModify" data-type="text" data-list="role">user</td>
<td>166</td>
<td>
<img class="information_modify" src="chemin" alt="img_modifier">
</td>
</tr>
<tr>
<td>1990-01-25 09:17</td>
<td class="canModify" data-type="text" data-list="prenom"></td>
<td class="canModify" data-type="text">Cameron May</td>
<td class="canModify" data-type="email">*****@mail.fr</td>
<td class="canModify" data-type="text" data-list="role">marketing</td>
<td>1736</td>
<td>
<img class="information_modify" src="chemin" alt="img_modifier">
</td>
</tr>
<tr>
<td>1980-01-17 14:01</td>
<td class="canModify" data-type="text" data-list="prenom"></td>
<td class="canModify" data-type="text">Carol Vance</td>
<td class="canModify" data-type="email">*****@mail.fr</td>
<td class="canModify" data-type="text" data-list="role">sales</td>
<td>564</td>
<td>
<img class="information_modify" src="chemin" alt="img_modifier">
</td>
</tr>
<tr>
<td>1999-08-01 09:44</td>
<td class="canModify" data-type="text" data-list="prenom"></td>
<td class="canModify" data-type="text">Megan Fraser</td>
<td class="canModify" data-type="email">*****@mail.fr</td>
<td class="canModify" data-type="text" data-list="role">user</td>
<td>1804</td>
<td>
<img class="information_modify" src="chemin" alt="img_modifier">
</td>
</tr>
<tr>
<td>1979-11-30 12:21</td>
<td class="canModify" data-type="text" data-list="prenom"></td>
<td class="canModify" data-type="text">Sarah Paterson</td>
<td class="canModify" data-type="email">*****@mail.fr</td>
<td class="canModify" data-type="text" data-list="role">executive officer</td>
<td>1164</td>
<td>
<img class="information_modify" src="chemin" alt="img_modifier">
</td>
</tr>
<tr>
<td>1986-10-08 15:25</td>
<td class="canModify" data-type="text" data-list="prenom"></td>
<td class="canModify" data-type="text">Nicholas Morgan</td>
<td class="canModify" data-type="email">*****@mail.fr</td>
<td class="canModify" data-type="text" data-list="role">admin</td>
<td>733</td>
<td>
<img class="information_modify" src="chemin" alt="img_modifier">
</td>
</tr>
<tr>
<td>1978-07-19 09:00</td>
<td class="canModify" data-type="text" data-list="prenom"></td>
<td class="canModify" data-type="text">Sam Abraham</td>
<td class="canModify" data-type="email">*****@mail.fr</td>
<td class="canModify" data-type="text" data-list="role">superuser</td>
<td>1440</td>
<td>
<img class="information_modify" src="chemin" alt="img_modifier">
</td>
</tr>
</tbody>
</table>

JS Fiddle demo.

上面的方法,虽然,似乎有点不必要;因为用户只有真的需要改变的错觉来启用编辑。考虑到这一点,下面的方法在表格单元格中保留<input>元素,并简单地更改它们的样式以指示编辑的可能性:

const editToggle = (evt) => {
let toggle = evt.currentTarget,
row = toggle.closest('tr');
row.classList.toggle('editInProgress');
row.closest('tbody').classList.toggle('editInProgress');
// here we select, and then iterate over, the NodeList of all <input>
// elements in the current <tr> element:
row.querySelectorAll('input').forEach(
// passing in a reference to the current element (here: <input>) to
// the body of the anonymous Arrow function:
(el) => {
// retrieving the value of the current <input>, and using
// String.prototype.trim() to remove leading, and trailing,
// white-space:
let value = el.value.trim(),
// retrieving the <datalist> element associated with
// the current <input>:
dataList = el.list,
// if the dataList is truthy (so not null, undefined, false...), we
// use the conditional ('ternary') operator to return an Array of
// the text of each <option> of the <datalist>; otherwise we return
// an empty Array:
dataListOptions = dataList ? [...dataList.options].map((opt) => opt.text) : [];

// if the dataList is truthy (as above), and the value is not an empty string,
// and if the dataListOptions Array does not include the current value:
if (dataList && value.length > 0 && !dataListOptions.includes(value)) {
// we append a new <option> element to the <datalist>, with its
// text (and implicit value) set to the value of the current <input>:
dataList.append(new Option(value));
}

// if the ancestor <tr> has the class of 'editInProgress':
if (row.classList.contains('editInProgress')) {
// we remove the 'readonly' attribute:
el.removeAttribute('readonly');
} else {
// otherwise, we set the readonly attribute:
el.setAttribute('readonly', '');
}
})
},
// retrieving the NodeList of all elements with the class of 'information_modify':
editButtons = document.querySelectorAll('.information_modify');
// iterating over that NodeList using NodeList.prototype.forEach():
editButtons.forEach(
// binding the editToggle() function as the event-handler for the 'click' event:
(button) => button.addEventListener('click', editToggle)
);
*,
::before,
::after {
box-sizing: border-box;
font-family: Roboto, Montserrat, system-ui;
font-size: 16px;
font-weight: 400;
line-height: 1.4;
margin: 0;
padding: 0;
}
table {
border-collapse: collapse;
margin-block: 1em;
margin-inline: auto;
table-layout: fixed;
width: clamp(50em, 80vw, 1000px);
}
th {
font-weight: bold;
}
th,
td {
border-block-end: 2px solid #000;
padding-block: 0.25em;
padding-inline: 0.5em;
}
.information_modify {
aspect-ratio: 1;
border: 1px solid rgb(200 200 200 / 0.7);
cursor: pointer;
display: inline-block;
height: 1em;
overflow: hidden;
}
img:empty {
--alpha: 0.5;
--accent-color: rgb(200 200 200 / var(--alpha));
background-image: repeating-linear-gradient(45deg, transparent 0 5px, var(--accent-color) 5px 8px);
}
img:empty:hover {
--alpha: 1;
}
td {
max-width: 10.5em;
}
td,
input {
color: inherit;
}
/* selecting any <td> or <th> element which is the second-child
of its parent: */
:is(td, th):nth-child(2) {
/* setting the width to 2em, this is to narrow the 'prenom'
cells: */
width: 5em;
}
/* selecting any <td> or <th> element which is the last-child
of its parent: */
:is(td, th):last-child {
/* setting its width to 2em, to narrow the last column;
I set the .information_modify <img> elements to be
1em in width and height, so adjust to your own requirements: */
width: 2em;
}
tr.editInProgress>td {
background-color: #ffa;
}
/* selecting all <td> or <input> elements which are within a <tr>
element that does not match the selector which is itself in
another ancestor which has the class of 'editInProgress': */
.editInProgress tr:not(.editInProgress) :is(td, input) {
/* trying to indicate a lack of interactivity: */
cursor: not-allowed;
opacity: 0.4;
pointer-events: none;
user-select: none;
}
input {
/* removing the border of all <input> elements, to remove the
impression of those elements being an <input>: */
border: unset;
outline: none;
/* setting the width of the elements to the minimum size of
either 10em or 95% of the parent: */
width: min(10em, 95%);
}
<datalist id="prenom">
<option>Mr</option>
<option>Ms</option>
<option>Mrs</option>
</datalist>
<datalist id="role">
<option>admin</option>
<option>analyst</option>
<option>executive officer</option>
<option>marketing</option>
<option>sales</option>
<option>superuser</option>
<option>user</option>
</datalist>
<table>
<caption> Liste des utilisateurs </caption>
<thead>
<tr class="title">
<th>Date de création</th>
<th>Prénom</th>
<th>Nom d'utilisateur</th>
<th>Mail</th>
<th>Rôle</th>
<th>Id</th>
<th></th>
</tr>
</thead>
<tbody>
<tr>
<td>1994-01-31 12:16</td>
<td class="canModify" data-type="text" data-list="prenom">
<input type="text" list="prenom" readonly>
</td>
<td class="canModify" data-type="text">
<input type="text" value="Anthony Sutherland" readonly>
</td>
<td class="canModify" data-type="email">
<input type="email" value="*****@mail.fr" readonly>
</td>
<td class="canModify" data-type="text" data-list="role">
<input type="text" list="role" value="user" readonly>
</td>
<td>1664</td>
<td>
<img class="information_modify" src="chemin" alt="img_modifier">
</td>
</tr>
<tr>
<td>1982-02-19 09:12</td>
<td class="canModify" data-type="text" data-list="prenom">
<input type="text" list="prenom" readonly>
</td>
<td class="canModify" data-type="text">
<input type="text" value="Owen Ince" readonly>
</td>
<td class="canModify" data-type="email">
<input type="email" value="*****@mail.fr" readonly>
</td>
<td class="canModify" data-type="text" data-list="role">
<input type="text" list="role" value="analyst" readonly>
</td>
<td>166</td>
<td>
<img class="information_modify" src="chemin" alt="img_modifier">
</td>
</tr>
<tr>
<td>1990-01-25 09:17</td>
<td class="canModify" data-type="text" data-list="prenom">
<input type="text" list="prenom" readonly>
</td>
<td class="canModify" data-type="text">
<input type="text" value="Sonia Hardacre" readonly>
</td>
<td class="canModify" data-type="email">
<input type="email" value="*****@mail.fr" readonly>
</td>
<td class="canModify" data-type="text" data-list="role">
<input type="text" list="role" value="analyst" readonly>
</td>
<td>1736</td>
<td>
<img class="information_modify" src="chemin" alt="img_modifier">
</td>
</tr>
<tr>
<td>1980-01-17 14:01</td>
<td class="canModify" data-type="text" data-list="prenom">
<input type="text" list="prenom" readonly>
</td>
<td class="canModify" data-type="text">
<input type="text" value="Liam Knox" readonly>
</td>
<td class="canModify" data-type="email">
<input type="email" value="*****@mail.fr" readonly>
</td>
<td class="canModify" data-type="text" data-list="role">
<input type="text" list="role" value="superuser" readonly>
</td>
<td>564</td>
<td>
<img class="information_modify" src="chemin" alt="img_modifier">
</td>
</tr>
<tr>
<td>1999-08-01 09:44</td>
<td class="canModify" data-type="text" data-list="prenom">
<input type="text" list="prenom" readonly>
</td>
<td class="canModify" data-type="text">
<input type="text" value="Stephanie Mathis" readonly>
</td>
<td class="canModify" data-type="email">
<input type="email" value="*****@mail.fr" readonly>
</td>
<td class="canModify" data-type="text" data-list="role">
<input type="text" list="role" value="admin" readonly>
</td>
<td>1804</td>
<td>
<img class="information_modify" src="chemin" alt="img_modifier">
</td>
</tr>
<tr>
<td>1979-11-30 12:21</td>
<td class="canModify" data-type="text" data-list="prenom">
<input type="text" list="prenom" readonly>
</td>
<td class="canModify" data-type="text">
<input type="text" value="Kevin Jackson" readonly>
</td>
<td class="canModify" data-type="email">
<input type="email" value="*****@mail.fr" readonly>
</td>
<td class="canModify" data-type="text" data-list="role">
<input type="text" list="role" value="analyst" readonly>
</td>
<td>1164</td>
<td>
<img class="information_modify" src="chemin" alt="img_modifier">
</td>
</tr>
<tr>
<td>1986-10-08 15:25</td>
<td class="canModify" data-type="text" data-list="prenom">
<input type="text" list="prenom" readonly>
</td>
<td class="canModify" data-type="text">
<input type="text" value="Sonia Hardacre" readonly>
</td>
<td class="canModify" data-type="email">
<input type="email" value="*****@mail.fr" readonly>
</td>
<td class="canModify" data-type="text" data-list="role">
<input type="text" list="role" value="superuser" readonly>
</td>
<td>733</td>
<td>
<img class="information_modify" src="chemin" alt="img_modifier">
</td>
</tr>
<tr>
<td>1978-07-19 09:00</td>
<td class="canModify" data-type="text" data-list="prenom">
<input type="text" list="prenom" readonly>
</td>
<td class="canModify" data-type="text">
<input type="text" value="Trevor Lawrence" readonly>
</td>
<td class="canModify" data-type="email">
<input type="email" value="*****@mail.fr" readonly>
</td>
<td class="canModify" data-type="text" data-list="role">
<input type="text" list="role" value="user" readonly>
</td>
<td>1440</td>
<td>
<img class="information_modify" src="chemin" alt="img_modifier">
</td>
</tr>
</tbody>
</table>

JS Fiddle demo.

对于第二种方法,有一个明显的问题,选择行为明显不同于如果单元格内容不是简单地"伪装"<td>元素,因为默认的(显然不可调整的)user-select: contain,这会导致用户选择包含在它开始的元素中,而不参与从其他地方开始的选择。

更不用说我选择在<input>元素上切换readonly属性的存在。

我认为,首选的方法将取决于个人偏好和项目需求。

相关内容

  • 没有找到相关文章

最新更新