1

I would like to combine an object, a path in a variable and a path, something like explained in https://stackoverflow.com/a/43849204/14480225 but with 3 "parts".

Here is minimal reproducible example: I want the combine() function to print selector[0].children[foo].textContent or selector[0].children[foo].children[0].textContent depending on bar value.

const selector = document.getElementsByClassName('main')
const foo = 0
let bar = ''

const toggle = () => {
  if (bar === '') bar = '.children[0]'
  else bar = ''
}

const test = () => {
  console.log(combine('selector[0].children[foo]', bar, '.textContent'))
  //I want a function like combine() that will print 'selector[0].children[foo].textContent' or 'selector[0].children[foo].children[0].textContent' depending on bar value.
}
<div class='main'>
  <h1>Hello<span>world</span></h1>
  <button onclick='toggle()'>toggle</button>
  <button onclick='test()'>test</button>
</div>

I have a class that is returning multiple similar properties (10). My constructor inputs a value that can be used to determine something like bar in the minimal example. Each property by just changing something like bar in the minimal example can have the intended output. I want something like this to be able to switch between different cases without having to write a massive switch for each bar cases so a lot of code duplication.

Hunam
  • 69
  • 1
  • 10
  • @ikiK - Thats what the OP want you to write for them ;-); The OP is making us read a 12 page novel (the other question) so they can be concise and leave out details I guess. For instance, in code the Op passes `selector[0].children` while stating "in my own code I have" `selector.children` - so, is the OP using a different selector "in my code" - we don't know. – Randy Casburn Feb 20 '21 at 00:36
  • This sounds like an [XY Problem](https://xyproblem.info)
. No description of a problem to solve other than gimmie code that does what I want. OP should define the problem rather than dictate how to write the solution. – Randy Casburn Feb 20 '21 at 00:38
  • Well i tried, i asked for end result, so i excepted to get if `x` then `?` if `y `then `?` . Should be clear after that judged on HTML. But I didn't get that... @RandyCasburn – ikiK Feb 20 '21 at 00:42
  • I get that and I appreciate you tried. But the problem isn't producing that string you can use as a way to get text from an element, let's say. It seems the problem statement might be something like this: Given selector and which child of the selector and which property name to retrieve, produce a function that takes those arguments and returns the property value. Something like that right? – Randy Casburn Feb 20 '21 at 00:45
  • @RandyCasburn yeah get that – ikiK Feb 20 '21 at 01:00
  • @ikiK - sorry - thought I was responding to the OP. Hope the OP reads my comments. – Randy Casburn Feb 20 '21 at 01:02
  • I have a simple solution that matches my comment description if the OP is interested. But if this is some odd homework thing or contrived job application test, whatevs. – Randy Casburn Feb 20 '21 at 01:04
  • Edited my question, is what you asked? – Hunam Feb 20 '21 at 01:18
  • Check out this [jsbin](https://jsbin.com/rakecezeze/1/edit?js,output) - you'll see the function takes three arguments. The first is a selector for the element you want to choose, the second is what are calling `bar` but continually referring to as `child[something]`, so the second is the # of the child to choose, and the third is the property you desire. You'll see how it works in the sample. If my code works for you, let me know and I'll post as an answer. – Randy Casburn Feb 20 '21 at 01:52
  • @RandyCasburn `bar` is not continually referring to as `.children[something]`, as I say in the question, it can also be nothing (represented by an empty string in my example) – Hunam Feb 20 '21 at 02:21
  • OK, that's easy enough - either nothing or a child at some depth - right? – Randy Casburn Feb 20 '21 at 02:26
  • @RandyCasburn yes – Hunam Feb 20 '21 at 02:28
  • Updated [jsbin](https://jsbin.com/joxicahuku/edit?js,output) – Randy Casburn Feb 20 '21 at 02:38

3 Answers3

1

Something like this? But please read about eval() here before using it: call javascript object method with a variable

const selector = document.getElementsByClassName('main')
const foo = 0
let bar = ''

const toggle = () => {
  if (bar === '') bar = '.children[0]'
  else bar = ''
}

function combine(){
let result = "selector[0].children[foo]"+bar+".textContent"
return result
}

const test = () => {
  console.log(eval(combine()))
  //I want a function like combine() that will print 'selector[0].children[foo].textContent' or 'selector[0].children[foo].children[0].textContent' depending on bar value.
}
<div class='main'>
  <h1>Hello<span>world</span></h1>
  <button onclick='toggle()'>toggle</button>
  <button onclick='test()'>test</button>
</div>

And here is a safe way of using it:

const selector = document.getElementsByClassName('main')
const foo = 0
let bar = ''

const toggle = () => {
  if (bar === '') bar = '.children[0]'
  else bar = ''
}

function combine(){
return Function('"use strict";return (selector[0].children[foo]'+bar+'.textContent)')()
}

const test = () => {
  console.log(combine())

}
<div class='main'>
  <h1>Hello<span>world</span></h1>
  <button onclick='toggle()'>toggle</button>
  <button onclick='test()'>test</button>
</div>

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/eval#never_use_eval

ikiK
  • 6,328
  • 4
  • 20
  • 40
  • Works, thanks! But I'm worried about security, how eval is dangerous, is eval dangerous in my context? I'm writting a Deno module fetching an open website. And also your two links are identical. – Hunam Feb 20 '21 at 01:50
  • @Weebonn Yeah i don't know about that, there is some talk about it yes, i was wondering how to do this and remembered i seen it somewhere, you should research more about it. – ikiK Feb 20 '21 at 02:11
  • Saw in the MDN doc that eval can be replaced by something else, but I could not get this working. See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/eval#never_use_eval! – Hunam Feb 20 '21 at 02:25
  • @Weebonn thanks for the link! Fixed it, check new snippet – ikiK Feb 20 '21 at 02:31
  • Works perfectly in a code snippet but not with deno-dom, I've submitted a new [issue](https://github.com/b-fuze/deno-dom/issues/34) on deno-dom. I'll pick this answer, safe and easy to use. – Hunam Feb 20 '21 at 14:39
  • 1
    UPDATE: not a deno-dom issue, not a deno issue either, this works in Deno: `(new Function("selector", "foo", '"use strict";return (selector[0].children[foo]' + bar + '.textContent)'))(selector, foo)`, see [this](https://github.com/b-fuze/deno-dom/issues/34) to see why. – Hunam Feb 22 '21 at 01:48
  • @Weebonn Interesting, Thanks for the info – ikiK Feb 22 '21 at 12:52
0

What you need to use is querySelector to find the element you are after.

Read about querySelector and querySelectorAll so you could understand better.

Using eval is not recommended and it is risky.

See if you like my idea below.

const selector = document.getElementsByClassName('main')
const path = ":nth-child(1)"

function getText(s, p) {
  if (s.length) // Is array
     return Array.from(s).map(x=> x.querySelector(p)?.textContent ?? "").join(" ");
  return  s.querySelector(p)?.textContent;
}

console.log(getText(selector, path))
<div class='main'>
  <h1>Hello<span>world</span></h1>
  <button onclick='toggle()'>toggle</button>
  <button onclick='test()'>test</button>
</div>
Alen.Toma
  • 4,684
  • 2
  • 14
  • 31
0

As we discussed in the comments, here is a solution that takes tree arguments and dynamically retrieves the property from the element of your choosing. Optionally, if you include the depth as number of children, the prop will be retrieved from that child.

Demonstration

Given:

The need to pass a CSS selector The number of the child element or empty string The property name for the value needed

When:

Passing those three given parmeters to a function

Then:

That function should return the value of the property identified from the child identified by number if provided of the element chosen with the selector.

Click test to run

// NOTE: No error checking - this is only a demonstration

function getValueFromDOM(selector, child, prop) {
  if (!child) {
    return document.querySelector(`${selector}`)[prop];
  } 
  return document.querySelector(`${selector}`).querySelector(`:nth-child(${child})`)[prop];
}

var children = [1, 2, 3, 4];
var output = document.getElementById('output');

function test() {
  children.forEach(child => {
    const value = getValueFromDOM('.main', child, 'textContent');
    output.innerHTML += `<p>${value}</p>`;
  });
  // With empty second argument - will print 'main'
  output.innerHTML += getValueFromDOM('.main', '', 'className')

}

document.querySelector('button:last-of-type').addEventListener('click', test);
<!DOCTYPE html>
<html>

<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width">
  <title>JS Bin</title>
</head>

<body>

  <div class='main'>
    <h4>Hello<span>world</span></h4>
    <button>toggle</button>
    <button>test</button>
    <div>Four</div>
    <div>Five</div>
    <div>Six</div>
  </div>
  <h3>Output:</h3>
  <div id="output"></div>
</body>

</html>
Randy Casburn
  • 13,840
  • 1
  • 16
  • 31