Getting Attributes from User submitted text! RegExp? - javascript

I am trying to pull the attributes out of piece of submitted text in Javascript and change it to an array.
So the user submits this:
<iframe src="http://www.stackoverflow.com/" width="123" height="123" frameborder="1"></iframe>
and I would get:
arr['src'] = http://www.stackoverflow.com/
arr['width'] = 123
arr['height'] = 123
arr['frameborder'] = 1
Just need a regexp I think but any help would be great!

I recommend to use a RegExp to parse user-inputed HTML, instead of creating a DOM object, because it's not desired to load external content (iframe, script, link, style, object, ...) when performing a "simple" task such as getting attribute values of a HTML string.
Using similar (although similarcontradiction?) methods as in my previous answer, I've created a function to match quoted attribute values. Both quoted, as non-quoted attributes are matched.
The code currently returns an object with attributes from the first tag, but it's easily extensible to retrieve all HTML elements (see bottom of answer).
Fiddle: http://jsfiddle.net/BP4nF/1/
// Example:
var htmlString = '<iframe src="http://www.stackoverflow.com/" width="123" height="123" frameborder="1" non-quoted=test></iframe>';
var arr = parseHTMLTag(htmlString);
//arr is the desired object. An easy method to verify:
alert(JSON.stringify(arr));
function parseHTMLTag(htmlString){
var tagPattern = /<[a-z]\S*(?:[^<>"']*(?:"[^"]*"|'[^']*'))*?[^<>]*(?:>|(?=<))/i;
var attPattern = /([-a-z0-9:._]+)\s*=(?:\s*(["'])((?:[^"']+|(?!\2).)*)\2|([^><\s]+))/ig;
// 1 = attribute, 2 = quote, 3 = value, 4=non-quoted value (either 3 or 4)
var tag = htmlString.match(tagPattern);
var attributes = {};
if(tag){ //If there's a tag match
tag = tag[0]; //Match the whole tag
var match;
while((match = attPattern.exec(tag)) !== null){
//match[1] = attribute, match[3] = value, match[4] = non-quoted value
attributes[match[1]] = match[3] || match[4];
}
}
return attributes;
}
The output of the example is equivalent to:
var arr = {
"src": "http://www.stackoverflow.com/",
"width": "123",
"height": "123",
"frameborder": "1",
"non-quoted": "test"
};
Extra: Modifying the function to get multiple matches (only showing code to update)
function parseHTMLTags(htmlString){
var tagPattern = /<([a-z]\S*)(?:[^<>"']*(?:"[^"]*"|'[^']*'))*?[^<>]*(?:>|(?=<))/ig;
// 1 = tag name
var attPattern = /([-a-z0-9:._]+)\s*=(?:\s*(["'])((?:[^"']+|(?!\2).)*)\2|([^><\s]+))/ig;
// 1 = attribute, 2 = quote, 3 = value, 4=non-quoted value (either 3 or 4)
var htmlObject = [];
var tag, match, attributes;
while(tag = tagPattern.exec(htmlString)){
attributes = {};
while(match = attPattern.exec(tag)){
attributes[match[1]] = match[3] || match[4];
}
htmlObject.push({
tagName: tag[1],
attributes: attributes
});
}
return htmlObject; //Array of all HTML elements
}

Assuming you're doing this client side, you're better off not using RegExp, but using the DOM:
var tmp = document.createElement("div");
tmp.innerHTML = userStr;
tmp = tmp.firstChild;
console.log(tmp.src);
console.log(tmp.width);
console.log(tmp.height);
console.log(tmp.frameBorder);
Just make sure you don't add the created element to the document without sanitizing it first. You might also need to loop over the created nodes until you get to an element node.

Assuming they will always enter an HTML element you could parse it and read the elements from the DOM, like so (untested):
var getAttributes = function(str) {
var a={}, div=document.createElement("div");
div.innerHTML = str;
var attrs=div.firstChild.attributes, len=attrs.length, i;
for (i=0; i<len; i++) {
a[attrs[i].nodeName] = attrs[i].nodeValue];
}
return a;
};
var x = getAttributes(inputStr);
x; // => {width:'123', height:123, src:'http://...', ...}

Instead of regexp, use pure JavaScript:
Grab iframe element:
var iframe = document.getElementsByTagName('iframe')[0];
and then access its properties using:
var arr = {
src : iframe.src,
width : iframe.width,
height : iframe.height,
frameborder : iframe.frameborder
};

I would personally do this with jQuery, if possible. With it, you can create a DOM element without actually injecting it into your page and creating a potential security hazard.
var userTxt = '<iframe src="http://www.stackoverflow.com/" width="123" height="123" frameborder="1"></iframe>';
var userInput = $(userTxt);
console.log(userInput.attr('src'));
console.log(userInput.attr('width'));
console.log(userInput.attr('height'));
console.log(userInput.attr('frameborder'));

Related

Why does javascript reads var as a string if i add '+' to it?

I have field names in my firestore document as
videolink1-"some video link"
videolink2-"some video link"
videolink3-"some video link"
I am using a for loop to get all videolinks present in the document.
if (doc.exists) {
for (var i = 1; i == videocount; i++) { //videocount is 3
var data = doc.data();
var videolink = data.videolink+i;
//creating new paragraph
var p = '<p class ="trackvideostyle">'+"Your Video Link : "+String(videolink)+'</p>\';
document.getElementById("btn").insertAdjacentHTML('beforebegin', p);
}
But this for loop is creating var values which are being read as string and firestore is returning me NaN as I dont have these fields :
data.videolink+1
data.videolink+2 //Firestore is returning null as i dont have these document fields
data.videolink+3
How can I write for loop so that var values are created like this and firestore reads it as:
videolink1
videolink2
videolink3
I think you could try something like this,
var videolink = data[`videolink${i}`];
Refer: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals
you are using "data.videolink+i" and javascript does not evaluate the value after the "." instead it is treated as the object's property. you need to use [] for evaluation. try this I hope this will work
if (doc.exists) {
for (var i = 1; i == videocount; i++) {
var data = doc.data();
var videolink = data[videolink+i];
//creating new paragraph
var p = '<p class ="trackvideostyle">'+"Your Video Link :
"+String(videolink)+'</p>\';
document.getElementById("btn").insertAdjacentHTML('beforebegin',
p);
}
Why it doesn't work
The dot operator (property accessor) has higher precendense, so it is evaluated first, so you get the value of the property and then you concatenate the value of your i variable.
What should you do
You can use another property accessor - square brackets, just like in arrays:
data['videolink']
You can build your property name inside of the square brackets:
data['videolink' + i]
or using template literals:
data[`videolink${i}`]
You can do this by using
1. template strings
..
var videolink = `${data.videolink}${i}`
..
2. concat()
..
var videolink = data.videolink.concat(i.toString());
..

Javascript createElement function

I have some code which is replicated throughout the main.js file
Javascript duplicates examples:
Example 1
for (i = userVote; i < 5; i++) {
var newVotesInput = document.createElement("input");
newVotesInput.id = "vote" + i;
newVotesInput.classList.add("vote", "displayn");
newVotesInput.type = "radio";
newVotesInput.name = "vote";
newVotesInput.value = i;
someElement.appendChild(newVotesInput);
}
Example 2
for (i = 1; i <= userVote; i++) {
var newVotesLabel = document.createElement("label");
newVotesLabel.id = "voteLabel" + i;
newVotesLabel.classList.add("voteHover");
newVotesLabel.htmlFor = "vote" + i;
someElement.appendChild(newVotesLabel);
}
Example 3
var newImg = document.createElement("img");
newImg.classList.add("babeImg", "boxsb", "leftIn");
newImg.id = "imgSrc";
newImg.src = jsonData.imgSrc;
someElement.appendChild(newImg);
Example 4
var newShuffle = document.createElement("img");
newShuffle.classList.add("shuffleImg");
newShuffle.id = "shuffleImg";
newShuffle.src = "assets/img/refresh.png";
someElement.appendChild(newShuffle);
Example 5
newImg.classList.add("babeImg", "boxsb", "leftIn");
newImg.id = "imgSrc";
newImg.src = jsonData.imgSrc;
imgInner.appendChild(newImg);
As you can see the examples do the same which is creating an element but there are several parameters which differs from example to example.
How do i make a function that i can use for all of them?
right now i only know how to build one for exactly one specific example like so ->
function createElement(element, id, cls, src){
var newElement = document.createElement(element);
element.id = id;
element.classList.add(cls);
element.src = src;
someElement.appendChild(element);
}
This is bascially Example 4.. but how do i make this more efficient and useable on all of the examples?
I would suggest the following function:
function createElement(type, attributes) {
var element = document.createElement(type);
for (var key in attributes) {
if (key == "class") {
element.classList.add.apply(element.classList, attributes[key]); // add all classes at once
} else {
element[key] = attributes[key];
}
}
someElement.appendChild(element);
}
The function takes a type (input, img, etc) and a list of attributes (i.e. an object/dictionary). We loop through the attributes and add them to the new element. The only special case is with classes, with use a different syntax (and we can have multiple classes). Here is how you can use it:
var input = createElement("input", {
"id": "myId",
"class": ["one-class", "another-class"],
"name": "someName"
});
Note that the class has an array as a value.
The result will be:
<input id="myId" class="one-class another-class" name="someName">
Here is the jsfiddle.
EDIT
To better understand: in JS, objects are like dictionaries, i.e. a bunch of key-value pairs (and prototypes, but this is not important for this discussion).
If you have an object with an id attribute, you can access it using one of the following syntax:
myObject.id // object style
myObject["id"] // dictionary style
The second syntax is very useful, since you can use variables to access object attributes:
var attr = "id";
myObject[attr];
Now, we can also iterate over an object, as we do with arrays. The only difference is that with arrays, the for in syntax returns the index, while with objects it returns the key or property name.
var myObject = { "id": 1, "name": "a name" }; // a basic object
for(var key in myObject) console.log(key, myObject[key]);
// will print
// id 1
// name a name
If you understand these two mecanics, the code I gave you will get much clearer.

InDesign Target XML Structure element by partial name

In my Structure pane there are some elements that their partial name is identical, eg. image01, image03, image03 etc.
I want to know if there is a way to access them via scripting using the itemByName() method, but by providing a partial name, like in CSS i can use
h1[rel*="external"]
Is there a similar way to do this in:
var items2 = items.xmlElements.itemByName("image");
You could try something like the code below. You can test against the markupTag.name properties with a regular expression. The regex is equivalent to something like /^image/ in your example (find image at the beginning of a string).
function itemsWithPartialName(item, partialName) {
var elems = item.xmlElements;
var result = [];
for (var i=0; i<elems.length; i++) {
var elem = elems[i];
var elemName = elem.markupTag.name;
var regex = new RegExp("^" + partialName);
if (regex.test(elemName)) {
result.push(elem);
}
}
return result;
}
itemsWithPartialName(/* some xml item */, 'image');
You can use an XPath:
var rootXE = app.activeDocument.xmlElements.item(0);
var tagXEs = rootXE.evaluateXPathExpression("//*[starts-with(local-name(),'image')]");

Create Element in Jquery

I would like to create element in Jquery/Javascript by using "div.someelement" like this
var SomeElement = $("div.someelement");
$( "#container" ).append( SomeElement );
But I don't want to copy element with the same class, I would like to create new one.
document.createElement is creating "<div.somelement>" instead of <div class="someelement">
I would use the following method to create elements on the fly
$("<div/>",{
"class" : "someelement",
// .. you can go on and add properties
"css" : {
"color" : "red"
},
"click" : function(){
alert("you just clicked me!!");
},
"data" : {
"foo" : "bar"
}
}).appendTo("#container");
Try this:
var $someelement = $('<div class="someelement"/>').appendTo('#container');
This will create a brand new element inside of #container and save it as $someelement for easy reference later.
http://api.jquery.com/jQuery/#jQuery2
UPDATE
You could clone the original then empty it out. This doesn't affect the original element at all.
var $someelement = $('div.someelement').clone().empty().appendTo('#container');
You can do this by the following:
var newElement = $('<div class="someelement"></div>');
$('#container').append(newElement);
or if you don't need the element you can directly append it:
$('#container').append('<div class="someelement"></div>');
According to the question you want to use a syntax like "div.someelement" to create an element.
In order to do that, you need to make your own parser.
It is very simple if that will be the exact syntax.
var str = "div.someelement",
parts = str.split("."),
elem = $("<" + parts.shift() + ">"),
cls;
while (cls = parts.shift())
elem.addClass(cls);
But if you're going to do this, you might as well use native methods.
var str = "div.someelement",
parts = str.split("."),
elem = document.createElement(parts.shift());
elem.className = parts.join(" ");
If you want to allow for full CSS syntax for creating an element, then you may want to look at the regex parser that Sizzle uses, and use it for your needs.
Use this:
var someElement = $("<div></div>");
someElement.addClass("someelement");
$("#container").append(someElement);
Or you can chain together the calls:
$("#container").append(
$("<div></div>")
.addClass("someelement")
);
EDIT:
Perhaps I misunderstood the question, maybe this will help. To create a new set of elements, use jQuery's clone method:
$("div.someelement").clone().appendTo("#container");
I would use zen coding for textarea as a starting point. Its syntax is close enough for what you are trying to do, its a well understood implementation. You should be able to invoke the transformation from a raw string rather than from a textarea with a little tweaking.
Since you are asking about creating an element from css syntax, you need to use a parser to interpret the syntax.
Here is an example you can build from. This will match an element name followed by id, class or other attributes. It won't cover some edge cases, but will work in most cases.
var elem_regex = /^(\w+)?|(#|\.)([^.#\[]+)|(\[[^\]]+?\])/g
Then make a function to get the parts and create an element.
function elementFromSelector(str) {
var match, parts = {}, quote_re = /^("|').+(\1)$/;
while (match = elem_regex.exec(str)) {
if (match[1])
parts.name = match[1];
else if (match[2] === ".") {
if (!parts.clss)
parts.clss = [];
parts.clss.push(match[3]);
} else if (match[2] === "#")
parts.id = match[3];
else if (match[4]) {
var attr_parts = match[4].slice(1,-1).split("="),
val = attr_parts.slice(1).join("");
parts[attr_parts[0]] = quote_re.test(val) ? val.slice(1,-1) : val;
}
else throw "Unknown match";
}
if (parts.name) {
var elem = document.createElement(parts.name);
delete parts.name;
for (var p in parts)
if (p === "clss")
elem.className = parts[p].join(" ");
else
elem[p] = parts[p];
return elem;
} else throw "No element name at beginning of string";
}
Then pass a proper string to the function, and it will return the element.
var str = 'input#the_id.firstClass.secondClass[type="text"][value="aValue"]';
var element = elementFromSelector(str);
Before creating the element, the parts look like this.
{
"name": "input",
"id": "the_id",
"clss": [
"firstClass",
"secondClass"
],
"type": "text",
"value": "aValue"
}
Then it uses that info to create the element that gets returned.
Simply create a new Element for jQuery:
var $newElement = $(document.createElement("div"));
$newElement.appendTo($("body"));
if you want to at attributes to de element simplie use:
$newElement.attr({
id : "someId",
"class" : "someClass"
});
Rember by class always use like this "class", because class is a reserved name

regular expression to extract all the attributes of a div

I have a requirement to extract the all the attributes of some tag. so i want to go for regex for this.for example <sometag attr1="val1" attr2="val2" ></sometag>. i want the attributes and values as name value pairs.
Any help appreciated
thanks
var s = '<sometag attr1="val1" attr2="val2" ></sometag>';
var reg = /\s(\w+?)="(.+?)"/g;
while( true ) {
var res = reg.exec( s );
if( res !== null ) {
alert( 'name = '+res[1] );
alert( 'value = '+res[2] );
} else {
break;
}
}
preg_match_all( '/\s(\w+?)="(.+?)"/', '<sometag attr1="val1" attr2="val2" ></sometag>', $matches );
for( $i = 0; $i < count( $matches[1] ); ++$i ) {
$name = $matches[1][$i];
$value = $matches[2][$i];
echo 'name'.$i.' = "'.$name.'", value'.$i.' = "'.$value.'", ';
}
result:
name0 = "attr1", value0 = "val1", name1 = "attr2", value1 = "val2",
of course you need to tweak this to fit your need and deal with bad html.
You could use [jquery][1] to get all attrubutes of an element
$('sometag').getAttributes();
http://plugins.jquery.com/project/getAttributes
A regex is not required. Much easier, use Element.attributes():
var attributes = element.attributes();
"Returns an array (NamedNodeMap) containing all the attributes defined for the element in question, including custom attributes." See the link for examples on how to access each attribute and it's value.
You can't do this in native JavaScript by using a regular expression. Using native JavaScript you have a couple of basic options. You can enumerate all of the node's properties and intelligently filter to get just the things you want, like:
window.extractAttributes = function(node) {
var attribString = "";
var template = document.createElement(node.tagName);
template.innerHTML = node.innerHTML;
for (var key in node) {
if (typeof node[key] == "string" && node[key] != "" && ! template[key]) {
if (attribString.length > 0) {
attribString += ", ";
}
attribString += key + "=" + node[key];
}
}
return attribString;
};
Or you can use Element.attributes to iterate the list of declared attributes (note that this may not detect non-standard attribute values that are added dynamically at runtime), like:
window.extractAttributesAlternate = function(node) {
var attribString = "";
for (var index = 0; index < node.attributes.length; index++) {
if (attribString.length > 0) {
attribString += ", ";
}
attribString += node.attributes[index].name+ "=" + node.attributes[index].nodeValue;
}
return attribString;
};
Note that the first approach may not pick up custom attributes that have been defined in the page markup, and that the second approach may not pick up custom attributes that have been defined dynamically by JavaScript on the page.
Which gives us option 3. You can enumerate the attributes both ways, and then merge the results. This has the benefit of being able to reliably pick up upon any custom attributes no matter when/how they were added to the element.
Here's an example of all 3 options: http://jsfiddle.net/cgj5G/3/
You can use XML parser, because provided input is well-formed XML.
Do not use Regex for this! Javacript's DOM already has all the information you need, easily accessible.
List all attributes of a DOM element:
var element = document.getElementById('myElementName');
var attributes = element.attributes;
for(var attr=0; attr<attributes.length; attr++) {
alert(attributes[attr].name+" = "+attributes[attr].nodeValue);
}
(tested above code in FF5, IE8, Opera11.5, Chrome12: Works in all of them, even with non-standard attributes)
Given the text of a single element which includes its start and end tags (equivalent of the element's outerHTML), the following function will return an object containing all of the attribute name=value pairs. Each attribute value can be single quoted, double quoted or un-quoted. Attribute values are optional and if not present will take on the attribute's name.
function getElemAttributes(elemText) {
// Regex to pick out start tag from start of element's HTML.
var re_start_tag = /^<\w+\b(?:\s+[\w\-.:]+(?:\s*=\s*(?:"[^"]*"|'[^']*'|[\w\-.:]+))?)*\s*\/?>/;
var start_tag = elemText.match(re_start_tag);
start_tag = start_tag ? start_tag[0] : '';
// Regex to pick out attribute name and (optional) value from start tag.
var re_attribs = /\s+([\w\-.:]+)(\s*=\s*(?:"([^"]*)"|'([^']*)'|([\w\-.:]+)))?/g;
var attribs = {}; // Store attribute name=value pairs in object.
var match = re_attribs.exec(start_tag);
while (match != null) {
var attrib = match[1]; // Attribute name in $1.
var value = match[1]; // Assume no value specified.
if (match[2]) { // If match[2] is set, then attribute has a value.
value = match[3] ? match[3] : // Attribute value is in $3, $4 or $5.
match[4] ? match[4] : match[5];
}
attribs[attrib] = value;
match = re_attribs.exec(start_tag);
}
return attribs;
}
Given this input:
<sometag attr1="val1" attr2='val2' attr3=val3 attr4 >TEST</sometag>
This is the output:
attrib = {
attr1: "val1",
attr2: "val2",
attr3: "val3",
attr4: "attr4"
};

Categories

Resources