Convert the text inside a span element into a key-value object in JavaScript

How can I extract and save the text from a span element as key value pairs within a div?

<div class="data-diff-span__composite-list-item__18c5zip data-diff-core__highlight-area__19c0zip">
  <div class="data-diff-basic__class-row__4ngp30a">
    <div class="data-diff-basic__class-header__4ngp30a">
      <span class="data-diff-basic__class-name__4ngp30a">ADDITIONAL_WRK</span>

      <span class="sp3-tag sp3-intent-danger">
        <span class="sp3-text-overflow-ellipsis sp3-fill">DECIMAL</span>
      </span>
    </div>
  </div>
</div>

<div class="data-diff-span__composite-list-item__18c5zip data-diff-core__highlight-area__19c0zip">
  <div class="data-diff-basic__class-row__4ngp30a">
    <div class="data-diff-basic__class-header__4ngp30a">
      <span class="data-diff-basic__class-name__4ngp30a"> AFFECTMRAPPLIC</span>

      <span class="sp3-tag sp3-intent-danger">
        <span class="sp3-text-overflow-ellipsis sp3-fill"> DECIMAL</span>
      </span>
    </div>
  </div>
</div>

<div class="data-diff-span__composite-list-item__18c5zip data-diff-core__highlight-area__19c0zip">
  <div class="data-diff-basic__class-row__4ngp30a">
    <div class="data-diff-basic__class-header__4ngp30a">
      <span class="data-diff-basic__class-name__4ngp30a">APPROVED_ON</span>

      <span class="sp3-tag sp3-intent-danger">
        <span class="sp3-text-overflow-ellipsis sp3-fill">TIMESTAMP</span>
      </span>
    </div>
  </div>
</div>

<div class="data-diff-span__composite-list-item__18c5zip data-diff-core__highlight-area__19c0zip">
  <div class="data-diff-basic__class-row__4ngp30a">
    <div class="data-diff-basic__class-header__4ngp30a">
      <span class="data-diff-basic__class-name__4ngp30a">COMPOSITE</span>
    </div>
  </div>
</div>

To individually access them, you can use the following:

// First span
var firstSpan= document.getElementsByClassName( 'data-diff-basic__class-name__4ngp30a' )
var firstSpanArray = [];
for ( var i = 0; i < firstSpan.length; ++i ) {
    firstSpanArray.push( firstSpan[i].innerText );
}

// Second span
var secondSpan= document.getElementsByClassName( 'sp3-text-overflow-ellipsis sp3-fill' )
var secondSpanArray = [];
for ( var i = 0; i < secondSpan.length; ++i ) {
    secondSpanArray.push( secondSpan[i].innerText);
}

Now, I want to combine firstSpanArray and secondSpanArray using their index into a key value JavaScript object. However, some spans may not have a corresponding second span, leading to an index mismatch.

Can anyone help me modify this query to achieve the following result:

spanCollection = {
  "ADDITIONAL_WRK":"DECIMAL",
  "AFFECTMRAPPLIC":"DECIMAL", 
  "APPROVED_ON":"TIMESTAMP",
  "COMPOSITE":""
}

Answer №1

To achieve this, you can select the second div in the first loop by checking with the parent element if it exists. If there is no parent (result 'null'), then set the value to an empty string.

Here's a working example:

var firstSpan = document.getElementsByClassName('data-diff-basic__class-name__4ngp30a');
var spanCollection = {};

for (var i = 0; i < firstSpan.length; ++i) {
  var value_span = firstSpan[i].parentNode.querySelector('.sp3-text-overflow-ellipsis.sp3-fill');
  var span_value = value_span ? value_span.innerText : '';
  var span_key = firstSpan[i].innerText;
  spanCollection[span_key] = span_value;
}

console.log(spanCollection);
[...]

Answer №2

There are multiple ways to accomplish the task at hand.

One method is to use a reduce based approach, where you iterate through an array of nodes obtained from a queried NodeList with each node belonging to the class

.data-diff-basic__class-name__4ngp30a
, these nodes contain the key-name information.

During each iteration step, you would...

  • Retrieve the key-name from the current node being processed.

    const key = keyNode.textContent.trim();
    
  • Fetch the corresponding value-containing node related to each key-containing node. If the value data exists, extract it. Otherwise, default to an empty string. To do this, you can use a combination of Element::querySelector and the optional chaining operator / ?.

    const value = keyNode.parentNode.querySelector('.sp3-fill')?.textContent.trim() || '';
    

Throughout the entire reduce process, you would aggregate key-value pairs into an initially empty object using Object.assign. The return value of the reducer function would be this object containing the aggregated key-value pairs...

const spanCollection = [
  ...document.querySelectorAll('.data-diff-basic__class-name__4ngp30a')
]
.reduce((result, keyNode) => {

  const key = keyNode
    .textContent.trim();
  const value = keyNode
    .parentNode.querySelector('.sp3-fill')?.textContent.trim() || '';

  return Object.assign(result, { [ key ]: value });

}, {});

console.log({ spanCollection });
.as-console-wrapper { min-height: 100%!important; top: 0; }
<div class="data-diff-span__composite-list-item__18c5zip data-diff-core__highlight-area__19c0zip">
  <div class="data-diff-basic__class-row__4ngp30a">
    <div class="data-diff-basic__class-header__4ngp30a">
      <span class="data-diff-basic__class-name__4ngp30a">ADDITIONAL_WRK</span>

      <span class="sp3-tag sp3-intent-danger">
        <span class="sp3-text-overflow-ellipsis sp3-fill">DECIMAL</span>
      </span>
    </div>
  </div>
</div>

... (additional HTML code follows) ...

</div>

Another approach could also utilize reduce, but in this case, the data to be reduced is an array of nodes where the text content of each node contains the complete key-value data as a single string after trimming, in the form...

'a_one_word_key     and a lot of value data'

Taking this example value, split using a regex like ...

'a_one_word_key     and a lot of value data'.split(/(\s+)/);

... which preserves the whitespace sequences during splitting, resulting in an array like...

['a_one_word_key', '     ', 'and', ' ', 'a', ' ', 'lot', ' ', 'of', ' ', 'value', ' ', 'data']

By utilizing array destructuring, one can access the necessary key-value data as follows...

const [key, _, ...valueData] = keyValueNode.textContent.trim().split(/(\s+)/);

The variable key will always exist, the underscore represents the optional first split whitespace sequence that is not needed. The valueData array, accessed using the rest property syntax, will default to an empty array regardless of the source data, ensuring defined key-value pairs.

In the object aggregation process similar to the first reduce example, you would now do...

Object.assign(result, { [ key ]: valueData.join('') });

The full example code for this second solution looks as follows...

const spanCollection = [
  ...document.querySelectorAll('.data-diff-basic__class-header__4ngp30a')
]
.reduce((result, keyValueNode) => {

  const [key, _, ...valueData] = keyValueNode.textContent.trim().split(/(\s+)/);

  return Object.assign(result, { [ key ]: valueData.join('') });

}, {});

console.log({ spanCollection });
.as-console-wrapper { min-height: 100%!important; top: 0; }
<div class="data-diff-span__composite-list-item__18c5zip data-diff-core__highlight-area__19c0zip">
  <div class="data-diff-basic__class-row__4ngp30a">
    <div class="data-diff-basic__class-header__4ngp30a">
      <span class="data-diff-basic__class-name__4ngp30a">ADDITIONAL_WRK</span>

      <span class="sp3-tag sp3-intent-danger">
        <span class="sp3-text-overflow-ellipsis sp3-fill">DECIMAL</span>
      </span>
    </div>
  </div>
</div>

... (more HTML code here) ...

</div>

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Tips for locating the previous CSS value before it was altered by javascript code

I am working on adjusting the CSS variables provided by the system using JavaScript with the following code: document.body.style.setProperty("--rh__primary-base", "rgba(254,80,0)"); However, when I inspect the element, I can see that t ...

Updating HTML Pages with Dynamic Content

Dealing with a massive project consisting of 50,000 pages (20,000 aspx forms, 10,000 asp forms, and 10,000 html pages) can be overwhelming. With only 2 days to complete the task of adding content after the body tag on all pages, I am seeking advice on ho ...

Add axios requests to the axios.all array

Good evening. I am currently trying to insert an array into the axios.all([]) structure! This is the code snippet I am working on: app2.js new Vue({ el: '#central', data: { estilo: 'resize:none;text-align:center;color: ...

Validation of phone numbers based on country codes

How can I validate phone numbers based on the selected country in Angular? Are there any Angular packages specifically for this task? I've attempted to use regex, but it only works for certain countries. I need a solution that can validate mobile an ...

Using the setTimeout function in Vue.js to trigger the play of an audio file at a specified

I have set up a Vue-audio player in my Vue.js application using the AudioPlayer component. This is my code: <template> <vue-audio id = "myAudio" :file= "file1"/> </template> <script> import VueAudio from 'vue-audio'; ...

Customize the appearance of Woocommerce's blockUi feature with your

During an Ajax request, blockUI adds a style to the blocks of the checkout form and cart with "background: '#fff'". However, my entire website theme is black and I do not want the background color of the blocks to be white. Is there a way to remo ...

Unable to launch React Native project

Error: Module Not Found Cannot find module 'C:\Users\Admin\AppData\Local\npm-cache\_npx\7930a8670f922cdb\node_modules\@babel\parser\lib\index.js'. Please make sure that your package.jso ...

Utilizing autosuggest in combination with jQuery ajax consistently generates suggestions with a delay of 1 keystroke

I'm currently working on creating an autosuggest feature for a search box, but I've run into a problem. The suggestions that are displayed don't seem to match the current keystroke input (they keep showing suggestions based on the previous k ...

Utilize the useRef hook in React to enable copying text to the clipboard

Looking to implement a copy to clipboard functionality in React using the useRef hook? Want to accomplish this without relying on any additional libraries? Take a look at my code snippet below. Currently, I'm encountering an error stating myRef.curren ...

"Failure to manipulate the style of an HTML element using Vue's

<vs-tr :key="i" v-for="(daydatasT, i) in daydatas"> <vs-td>{{ daydatasT.Machinecd }}</vs-td> <vs-td>{{ daydatasT.Checkdt }}</vs-td> <vs-td>{{ daydatasT.CheckItemnm }}< ...

Sending a directive as an argument to a parent directive function

edit: I made adjustments to the code based on stevuu's recommendation and included a plunkr link here Currently, my goal is to make a child directive invoke a method (resolve) through another directive all the way up to a parent directive. However, I ...

Using a CSS gradient with a variable in React.js - A guide to implementation

Looking to incorporate the following CSS property into the Navlink component. In the CSS file, the property is usually written like this using gradient. .ele { background-image: linear-gradient(45deg, #808080 25%, transparent 25%), li ...

Utilizing jQuery for interacting with iframes

My script functions perfectly on the page, but when I embed it using an iframe, the jQuery features stop working even though the script is written as usual. Even using $.noConflict(); does not resolve the issue. ...

Beware, search for DomNode!

I attempted to create a select menu using material-ui and React const SelectLevelButton = forwardRef((props, ref) => { const [stateLevel, setStateLevel] = useState({ level: "Easy" }); const [stateMenu, setStateMenu] = useState({ isOpen ...

How can I reset a CSS position sticky element using JavaScript?

I have created a page where each section fills the entire screen and is styled using CSS position: sticky; to create a cool layered effect. Check it out here: https://codesandbox.io/s/ecstatic-khayyam-cgql1?fontsize=14&hidenavigation=1&theme=dark ...

Stop the Sidebar from showing up on certain pages with Next.js

Currently, I am facing a small issue with my application. The problem lies in the sidebar that appears on my login.jsx page when I specifically do not want it there. However, I would like it to appear on all other pages except for this one. Is there a cond ...

I utilized the `<script src="sample.pdf"></script>` tag in my HTML code and surprisingly, the JavaScript within the PDF document was still able to execute

Recently, I encountered a situation where I included a PDF file with JavaScript code in the src attribute of a script tag in my code. Surprisingly, the JavaScript code executed without any issues. This made me wonder if I can use any type of file extension ...

Achieving Center Alignment for Material-UI's <Table> within a <div> using ReactJS

Currently, I am working with a Material-UI's <Table> embedded within a <div>. My goal is to center the <Table> while maintaining a fixed width. I want the table to remain centered in the browser, but if the browser window is minimize ...

Show the GitHub repositories of the user within a React application

Even though I am relatively new to React, I managed to create a GitHub search application. In this app, you can input a user's name in a search box and view their avatar, bio, username, etc. However, I'm facing an issue with fetching their reposi ...

What is the best way to link a multi-select element with an array of objects?

How can I bind a select element with a model to get/set the value and populate it from a list using angular 1? Currently, I'm able to bind it from UI to model, but not vice versa. What am I doing wrong? HTML: <div ng-controller="MyCtrl"> ...