简体   繁体   English

在 ReactJS 中使 UI 组件可选

[英]Making UI Components Selectable in ReactJS

Let's say I have a few UI Components in React JS, which render some HTML on screen, like below:假设我在 React JS 中有一些 UI 组件,它们在屏幕上呈现一些 HTML,如下所示:

<div class="content-block margin-10">
  <h1 class="block-head">Heading Text</h1>
  <div class="section-text bg-gray text-md">Section Text</div>
</div>

On screen, this is displayed as proper HTML.在屏幕上,这显示为正确的 HTML。 What I want is the ability for the user to be able to select (by clicking) this block, which should display the element tree along with the classes applied to each element on one side of the page.我想要的是用户能够 select(通过单击)这个块,它应该显示元素树以及应用于页面一侧每个元素的类。 Then, my goal is to allow users to change the classes applied to see how the UI changes with the change in classes.然后,我的目标是允许用户更改应用的类,以查看 UI 如何随着类的变化而变化。

I want to be able to do this react/javascript without using any library such as grapesjs.我希望能够在不使用任何库(例如grapesjs)的情况下执行此反应/javascript。

Can you please guide me on how I can go about achieving this?您能否指导我如何实现这一目标?

Update: One thing I did is that I added a function onClick={(e) => handleClick(e)} to the block div.更新:我做的一件事是在块 div 中添加了一个 function onClick={(e) => handleClick(e)}

Now within handleClick, I could get the element's classed:现在在 handleClick 中,我可以得到元素的分类:

const handleClick = (e) => {
    console.log('E', e.target.classList);
    e.target.classList.add(styles.borderline);
}

Now the problem is, if there are multiple blocks on the page, how will I know which block is selected?现在的问题是,如果页面上有多个块,我怎么知道选择了哪个块? Eventually, I want to save/replace the data related to the block in my database.最终,我想在我的数据库中保存/替换与块相关的数据。 So, I need to be able to know which block was updated, and save its data.所以,我需要能够知道哪个块被更新,并保存它的数据。

There are three parts to this solution:此解决方案分为三个部分:

  1. Stateful class values有状态的 class 值
  2. Displaying the tree显示树
  3. Binding the stateful class values to inputs将有状态的 class 值绑定到输入

(1) Using functional components with React hooks, you can initialize a variable for each default class name with the useState hook as follows: (1)使用带有 React 钩子的功能组件,您可以使用useState钩子为每个默认的 class 名称初始化一个变量,如下所示:

const [containerClass, setContainerClass] = useState("content-block margin-10");
const [headingClass, setHeadingClass] = useState("block-head");
const [sectionClass, setSectionClass] = useState("section-text bg-gray text-md");

This sets their default values to the values in the question description and ensures that when these values are updated, any HTML that depends on these values will re-render.这会将它们的默认值设置为问题描述中的值,并确保在更新这些值时,任何依赖于这些值的 HTML 都将重新呈现。 Thus, in order to bind these to the elements in question, we can use the following className s as opposed to their hard-coded counterparts:因此,为了将这些绑定到有问题的元素,我们可以使用以下className ,而不是硬编码的对应项:

<div className={containerClass}>
  <h1 className={headingClass}>Heading Text</h1>
  <div className={sectionClass}>Section Text</div>
</div>

(2) Displaying the tree requires three things of us: a boolean variable that indicates whether or not the tree is displayed, a click event (as noted in the question) that toggles this variable, and a JSX section that renders when the variable is true. (2)显示树需要我们做三件事:指示是否显示树的 boolean 变量,切换此变量的单击事件(如问题中所述),以及当变量为真的。

The variable itself can be a simple useState declaration like the ones above:变量本身可以是一个简单的useState声明,就像上面的那些:

const [treeIsDisplayed, setTreeIsDisplayed] = useState(false);

It can be toggled by using the following function as the container div 's onClick value:它可以通过使用以下 function 作为容器divonClick值来切换:

const toggleTree = () => setTreeIsDisplayed((prev) => !prev);

And we can render the tree conditionally like so:我们可以像这样有条件地渲染树:

{treeIsDisplayed && (
  <div>
    <p>
      {"<div class="}
      <input value={containerClass} />
      {"></div>"}
    </p>
    <p>
      &nbsp;&nbsp;&nbsp;&nbsp;{"<h1 class="}
      <input value={headingClass} />
      {"></h1>"}
    </p>
    <p>
      &nbsp;&nbsp;&nbsp;&nbsp;{"<div class="}
      <input value={sectionClass} />
      {"></div>"}
    </p>
    <p>{"</div>"}</p>
  </div>
)}

This renders as the following:这呈现如下:

渲染树

Note: The tree will display under the HTML by default, so some CSS will be needed to display it to the side.注意:树将默认显示HTML 下,因此需要一些 CSS 将其显示到侧面。

(3) (3)

We can now create functions to be utilized by the above inputs to set the class names dynamically:我们现在可以创建上述输入使用的函数来动态设置 class 名称:

const changeContainerClass = (e) => setContainerClass(e.target.value);
const changeHeadingClass = (e) => setHeadingClass(e.target.value);
const changeSectionClass = (e) => setSectionClass(e.target.value);

And we set the onChange value of each input to its corresponding change function.并且我们将每个输入的onChange值设置为其对应的变化function。

All together, the working solution constructed here looks like this:总之,这里构建的工作解决方案如下所示:

const App = () => {
  const [containerClass, setContainerClass] = useState("content-block margin-10");
  const [headingClass, setHeadingClass] = useState("block-head");
  const [sectionClass, setSectionClass] = useState("section-text bg-gray text-md");

  const [treeIsDisplayed, setTreeIsDisplayed] = useState(false);

  const toggleTree = () => setTreeIsDisplayed((prev) => !prev);

  const changeContainerClass = (e) => setContainerClass(e.target.value);
  const changeHeadingClass = (e) => setHeadingClass(e.target.value);
  const changeSectionClass = (e) => setSectionClass(e.target.value);

  return (
    <>
      <div className={containerClass} onClick={toggleTree}>
        <h1 className={headingClass}>Heading Text</h1>
        <div className={sectionClass}>Section Text</div>
      </div>
      {treeIsDisplayed && (
        <div>
          <p>
            {"<div class="}
            <input value={containerClass} onChange={changeContainerClass} />
            {"></div>"}
          </p>
          <p>
            &nbsp;&nbsp;&nbsp;&nbsp;{"<h1 class="}
            <input value={headingClass} onChange={changeHeadingClass} />
            {"></h1>"}
          </p>
          <p>
            &nbsp;&nbsp;&nbsp;&nbsp;{"<div class="}
            <input value={sectionClass} onChange={changeSectionClass} />
            {"></div>"}
          </p>
          <p>{"</div>"}</p>
        </div>
      )}
    </>
  )
}

 var elementTree; function convertToHTML(tree) { let elementTree = `<table>`; elementTree += ` <thead> <tr> <th>Element Tree</th> <th>Classes</th> </tr> </thead>`; tree.map((element, elementIndex) => { elementTree += ` <tr> <td>${element.element}</td> <td> <textarea onchange=changeClass(event) data-target='${elementIndex}'>${element.classList.join(" ")}</textarea> </td> </tr>`; }); elementTree += `</table>` return elementTree; } function changeClass(event) { let textArea = event.target; let oldClass = textArea.defaultValue.trim().split(" "); let newClass = textArea.value.trim().split(" "); let index = textArea.dataset.target; if (oldClass[0]) { elementTree[index].target.classList.remove(...oldClass) } elementTree[index].target.classList.add(...newClass) console.log(oldClass, newClass); } function getElementTree(event) { var target = event.target, elementTree = []; while (target) { let classList = target.classList; elementTree.unshift({ element: target.tagName, classList: target.classList.value.split(" "), target: target }); target = target.parentElement; } console.log(elementTree); return elementTree; } function showElementTree(tree) { document.getElementsByClassName("element-tree")[0].innerHTML = tree; } document.getElementsByClassName('element')[0].addEventListener('click', function(event) { elementTree = getElementTree(event); let elementTreeHTML = convertToHTML(elementTree); showElementTree(elementTreeHTML); }, false);
 .main { display: flex; }.main>div { border: 1px solid black; padding: 10px; } table, th, tr, td { padding: 5px; border: 1px solid black; border-collapse: collapse; }
 <div class="main"> <div class="element"> <div class="content-block margin-10"> <h1 class="block-head">Heading Text</h1> <div class="section-text bg-gray text-md"> <p>element tree</p> </div> </div> </div> <div class="element-tree"> </div> </div>

A vanilla JS solution would be to use eventHandlers and hold the target variable. vanilla JS 解决方案是使用 eventHandlers 并保存目标变量。 Whenever the class changes the associated classList property of the target object is changed每当 class 更改目标 object 的关联 classList 属性时

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM