I have a javaScript array of objects, where the objects are called text, where each object element contains information about an HTML element that looks like the following using the Chrome browser’s inspect Source Watch area:
text: attributes: NamedNodeMap 0: baseURI: "file:.../awesomplete_textarea.html" childNodes: NodeList [] firstChild: null isConnected: false lastChild: null localName: "label" name: "label" namespaceURI: null nextSibling: null nodeName: "label" nodeType: 2 nodeValue: "Alternative Rock" ownerDocument: document ownerElement: li parentElement: null parentNode: null prefix: null previousSibling: null specified: true textContent: "Alternative Rock" value: "Alternative Rock" __proto__: Attr } length: 1 label: { same content as 0, above } __proto__: NamedNodeMap } label: "Alternative Rock" tagName: "LI" value: "Alternative Rock (Alternative)" length: 16 __proto__: String
Note: While the attributes member of text object (above) only contains the information for the label attribute, it could also contain other attributes items, such as a style and/or class, which were copied from a set of <li>
tags in my web-page, in which case there would be additional entries in the text object and attributes member for those attributes items.
When the information <li>
tags are collected, the tag’s attributes are copied into text object using the copyAttributes function that is shown below, which works fine.
However, when later try to use the same copyAttributes function to copy the text.attributes to the attr variable to then create a new HTML element, I get this error:
mycode.js:98 Uncaught TypeError: to.setNamedItem is not a function at copyAttributes (mycode.js:98) at Function._.ITEM (mycode.js:940) at _.item (mycode_textarea.html:625) at mycode.js:826 at Array.forEach (<anonymous>) at _.evaluate (mycode.js:825)
The following code shows how this call is made, and what I’m trying to with the information copied into attr object variable.
function copyAttributes( from, to ) { var attr; for( const [ key, value ] of Object.entries( from ) ) { if( !isNaN( key ) ) { // Only add the named attributes ... let name = from[ key ].name;; // get the attribute's name if( to[ name ] === undefined ) { attr = document.createAttribute( name ); // Create the attribute. attr.value = value.value; // Assign its value. to.setNamedItem( attr ); // Add it to to } } } } var tagName = 'li'; var attribute = {}; // Copy the text.attributes from the text object to attributes. copyAttributes( text.attributes, attributes ); // Add the information in the {...} object to the attributes object ... Object.assign( attributes, attributes, { innerHTML: label, // Add these to those just copied. 'role': 'option', 'id': 'list_' + this.count + '_item_' + item_id, 'value': value } ); // Create the new `<li>` with the original tag's attributes and those added, above. $.create( tagName, attributes );
The problem seems to be that the to
parameter of the copyAttributes function isn’t the NamedNodeMap type, so it doesn’t support the setNamedItem
method.
How can I create an empty attributes variable of this type?
Or is there a better/easier way to populate the attributes variable with the test.attributes information?
Thanks.
Advertisement
Answer
Here is a codepen of a working version of the drow-down combobox that properly created the li tag elements and copies the desired attributes into it.
This create function is from the Awesomplete plug-in, but it tha been modified to not set the focud on the newly created element.
$.create = function( tag, o ) { var element = document.createElement( tag ); for( var i in o ) { var val = o[ i ]; if( i === 'inside' ) { $( val ).appendChild( element ); } else if( i === 'around' ) { var ref = $( val ); ref.parentNode.insertBefore( element, ref ); element.appendChild( ref ); if( ref.getAttribute( 'autofocus' ) != null ) { ref.focus(); } } else if( i in element ) { element[ i ] = val; } else { element.setAttribute( i, val ); } } return element; };
This is my modified code that calls the above create function and assign the attributes.
if( tagName === 'LI' ) { matched = ( inputValue = ( ', ' + me.input.value.trim().toLowerCase() + ',' ) ) // Normalize input.value so that the first item can be found. .includes( ', ' + value.toLowerCase() + ',' ) || // Find the normalized value in the normalized input.value ... inputValue.includes( ', ' + label.toLowerCase() + ',' ); // Find the normalized label in the normalized input.value ... Object.assign( attributes, attributes, { 'role': 'option', 'id': 'awesomplete_list_' + this.count + '_item_' + item_id, 'value': value, // Give every option ( li ) a value attribute. 'aria-selected': matched.toString() } ); // If a match was found then set aria-selected to 'true' // else set area-selected to 'false'. } else { matched = false; } newTag = $.create( tagName ); newTag.innerHTML = label; if( text.attributes.length ) { // Now copy the attributes ... copyAttributes( text.attributes, newTag.attributes ); // Add the rest of the attributes ... copyAttributes( attributes, newTag.attributes, true ); } return newTag;