0

I am new to Javascript development.

I am trying to assign HTML elements IDs stored in an array to shorthands to be used in my function later.

So that instead of writing :

let addprop = document.querySelector(`#addprop`);
let readprop = document.querySelector(`#readprop`);
let editprop = document.querySelector(`#editprop`);
let footer = document.querySelector(`#footer`);
let association = document.querySelector(`#association`);

I can attribute elements ids that i store in an array like this :

let arrayElements = ["addprop", "readprop", "editprop", "footer", "association"] ;
arrayElements.forEach(el => { return(new Function (`${el} = document.querySelector("#${el}");`)()); });

Now, this bit of code works but from what I read here : Execute JavaScript code stored as a string

This is probably not a good way to do it and also declares global variables. One problem I encountered is that if I try to directly execute the assignment like this :

el = document.querySelector(`#${el}`);

Then the el value takes the value of the named access ID element (https://html.spec.whatwg.org/multipage/window-object.html#named-access-on-the-window-object) and breaks the code. So I resorted to generate a string first then execute it.

I could simply assign each shorthand manually but I spent way too much time trying to make this work and am now left curious as to what would be a good solution or approach for this.

And would the scope limitations for loops simply forbid me to do this without using global variables ?

edit : switched the working code in one line

Possible answer : 1 - does it matter to declare global variables like that ? As these variables already exist globally because of browsers named access for elements IDs.

2 - By kiranvj's answer, a solution can be to store in an object structured as keys being the shortcuts and the full strings being the values, and calling the shortcuts with the object[key] method ; or using destructuring to assign the values to variable directly with :

const {addprop, readprop, editprop, idfooter, assocpatients} = elements; 

I feel like I am missing something on this last one but it also seems to work.

In the end I will stick with my first code as condensing the function in one line seems to negate the risks of cross site scripting (?), and global values for the variables assigned though this method anyway already exist because of named access.

yericus
  • 31
  • 3
  • I don't understand what is your goal ? as you said you have limited in scope of variable. – H.T Dec 09 '20 at 06:52
  • Well I wanted to have a short way to assign variables to shorthand to be used in my function. And went on because I wanted to make it work and learn. Thinking about it some more, all those shorthands I define actually exist as global variables already because of named spaces, so that makes it a bit less of an issue maybe ? – yericus Dec 09 '20 at 07:36

2 Answers2

0

If you are concerned about global scope, you can try something like below. Use forEach instead of map . map also work but since you are not handling the return of map, forEach would be a better choice.

let arrayElements = ["addprop", "readprop", "editprop", "footer", "association"];
let elements = {};
arrayElements.forEach(el => elements[el] = document.querySelector(`#${el}`));

// access variables like elements.ID-NAME
console.log(elements);
<div id="addprop"></div>
<div id="readprop"></div>

Object destructing can be used if you know the object key name.

example : let {addprop} = element;

Another thing which you might be interested is Automatic global variables

This means a new variable (scoped to window) with the name of element id is created for all the elements in page. See the html5 spec. I would not recommend using it though.

So you don't have to call like document.querySelector('addprop')

addprop variable will have the DOM object.

See this example

// these works due to automatic global varaibles binding
alert(addprop); 
console.log(addprop);
<div id="addprop">Some contents</div>
kiranvj
  • 32,342
  • 7
  • 71
  • 76
  • Thank you I will try that after a welcome break ... As I answered a previous comment, it occurred to me that all these shorthand I declare as global variables actually already exist as named access Ids at the global level so I wonder if that mitigates the issue. I began to do that because named access elements behave differently than a querySelector declaration when used in an array like that : [footer, association].map(el => el.classList.remove(`hide`)) The named access would come with nested arrays that would mess up this line when the document.querySelector one would work fine. – yericus Dec 09 '20 at 07:47
  • So we store the resulting value as a key - value pair. and use the object to quickly call an id. Thanks for your help ! I will see if having global variables is really an issue ; in the end, all this is to avoid typing a very small amount of code but it as interesting to explore how that can be done ! – yericus Dec 09 '20 at 11:53
  • we can use object de structuring as you had edited and then removed by assigning the values like that : const {addprop, readprop, editprop, idfooter, assocpatients} = elements; There is probably a way to go from the original array to this but I can't find it easily right now. – yericus Dec 09 '20 at 12:33
0

You can create a dictionary with all the elements with ID and then destroy it into your variables, ignoring the unused ones.

function getAllElementsWithId() {
    let elements = {}
    for (let el of document.querySelectorAll('[id]')) {
        if (!(el.id in elements)) {
            elements[el.id] = el
        }
    }
    return elements
}


let { addprop, readprop, editprop, footer, association } = getAllElementsWithId()

This uses document.querySelectorAll (link to MDN) to get all elements with an ID. Notice that for big pages this could be a performance issue.

Also, what you would usually do is to add them into a container, in this case it seems like a dictionary.

let arrayElements = ["addprop", "readprop", "editprop", "footer", "association"]
let elementsId = Object.fromEntries(arrayElements.map(id => [id, document.getElementById(id)]))

This uses Object.fromEntries (link to MDN) to generate the dictionary. Also I'm using document.getElementById (link to MDN) instead of document.querySelector so you don't need to add the hashtag before the id.

Martí
  • 2,651
  • 1
  • 4
  • 11
  • Thanks, gave me some more perspective and methods on the subject. Going over the whole document to lit IDs might be overkill ? working with arrays and objects seems like an entire field of its own ah ! – yericus Dec 09 '20 at 10:38
  • It's an absolute overkill. It's not really something I'd use, but it's the exact response for your question. I'd personally would use a dictionary with the necessary elements if I need to relate to them by id or an array if I only need to have all of them in a list. – Martí Dec 09 '20 at 12:39
  • Also notice that the first code adds the names into the scope it's on, so you can use them directly. The second you'll need to do something like `elementsId.addprop` or `elementsId['addprop']` – Martí Dec 09 '20 at 12:41
  • I resorted to using my first function as these global variables anyway already exist with named access activate by default in all browsers now, and writing the function in one line would seem to make cross scripting impossible (?). Thanks for your help that was a good exercise to learn stuff ... though my project did not advance much today ah ! – yericus Dec 09 '20 at 12:57