How to combine multiple RegEx Objects without multiple try-catch functions - javascript

The variable htmlStr may contain different spellings of id:
var htmlStr = "<div id="demo_div"></div>";
var htmlStr = "<div id='demo_div'></div>";
var htmlStr = "<div id=demo_div class="demo"></div>";
var htmlStr = "<div id=demo_div></div>";
How can I write this differently without many try-catch functions? Can I combine the patterns? It works - but does not look pretty.
var idname;
try {
idname = /(id="(.*?)(\"))/g.exec(htmlStr)[2]
} catch (e) {
try {
idname = /(id='(.*?)(\'))/g.exec(htmlStr)[2]
} catch (e) {
try {
idname = /(id=(.*?)(\ ))/g.exec(htmlStr)[2]
} catch (e) {
try {
idname = /(id=(.*?)(\>))/g.exec(htmlStr)[2]
} catch (e) {
console.log(e);
}
}
}
}
console.log(idname);

You can do this without using regex by simply parsing the HTML.
const htmlStrings = [
'<div id="demo_div"></div>',
"<div id='demo_div'></div>",
"<div id=demo_div class='demo'></div>",
'<div data-id="not_a_real_id"></div>', //note: doesn't have an ID
"<div data-id=not_an_id ID= demo_div></div>",
"<div id= demo_div><span id=inner_id></span></div>"
];
function getId(html) {
const parser = document.createElement('div');
parser.innerHTML = html;
return parser.firstChild.id;
}
htmlStrings.forEach(x => console.log(getId(x)));
As you can see, you can create an element, put the HTML in it, then grab the first child and check it's ID. It works even if you have another type of attribute like a custom attribute called data-id or if the ID has any kind of capitalisation or even if that div has inner elements or anything else.
This technique won't work with invalid HTML or if you have multiple elements you want the ID of but this is simply to demonstrate it. Once it's parsed into a proper element, you can traverse its hierarchy as you see fit and perform any sort of extraction you need.

/id=["']?([^\s"'>]+)/g
This will match all four examples.

Related

how to get this json object table filter working

I have a website with a list of json objects arranged something like this:
[
{
"a": true or false,
"b": "information",
"c": "information",
"d": "information",
"e": "information"
},
...
]
The idea of this code is to print out all the objects on a table and have a checkbox which filters out the false objects out when needed. The site is supposed to just have the the table with unfiltered object on there, but after I added the checkbox event listener the full table list disappeared. When I check the checkbox I get the filtered objects and it keeps adding more and more of the same filtered content on the bottom of the table if I keep re-clicking it.
What am I doing wrong here? Here is the code I have:
var stuff = document.getElementById("stuff-info");
var ourRequest = new XMLHttpRequest();
ourRequest.open('GET', 'url');
ourRequest.onload = function() {
var ourData = JSON.parse(ourRequest.responseText);
renderHTML(ourData);
};
ourRequest.send();
function renderHTML(data) {
var htmlString = "";
var filteredData = data.filter(function(element) {
return element.a
});
var checkbox = document.querySelector("input[name=hide]");
checkbox.addEventListener('change', function() {
if (this.checked) {
for (i = 0; i < filteredData.length; i++) {
htmlString += "<table><tr><td>" + filteredData[i].b + "</td><td>" + filteredData[i].c + "</td><td>" + filteredData[i].d + "</td><td>" + filteredData[i].e + "</td></tr>"
}
} else {
for (i = 0; i < data.length; i++) {
htmlString += "<table><tr><td>" + data[i].b + "</td><td>" + data[i].c + "</td><td>" + data[i].d + "</td><td>" + data[i].e + "</td></tr>"
}
}
stuff.insertAdjacentHTML('beforeend', htmlString);
});
}
Might be easier to filter with CSS selector:
#filter:checked ~ table .filter { display: none }
<input type=checkbox id=filter> Filter
<table border=1>
<tr class=filter><td>1</td><td>a</td></tr>
<tr><td>2</td><td>b</td></tr>
<tr class=filter><td>3</td><td>c</td></tr>
<tr><td>4</td><td>d</td></tr>
</table>
after I added the checkbox event listener the full table list disappeared.
All of your logic for deciding what to render is trapped inside your onchange event, so nothing will be drawn until a checkbox is changed.
When I check the checkbox I get the filtered objects and it keeps adding more and more of the same filtered.
All of your html strings are generated with += against the original htmlString variable trapped in the closure. So yeah, it will just keep adding more and more rows. You are also inserting the udated strings into the dom without removing the old table(s), so this will be exponential growth.
I think there is a great case here for higher order functions instead of for loops, you can use the map array method to transform each item in the array into a string, instead of manually iterating. This is cleaner and more maintainable.
Notice that now that the rendering logic is not mixed together with the event logic, it would be much easier to reuse the render function with some different data or different events. It's also somewhat trivial to add more transformations or filters.
const ourRequest = new XMLHttpRequest();
ourRequest.onload = function() {
const ourData = JSON.parse(ourRequest.responseText);
initialRender(ourData);
};
ourRequest.open('GET', 'url');
ourRequest.send();
function filterAll() { return true; }
function filterA() { return element.a; }
function toRowString(item) {
return `
<tr>
<td>${item.a}</td>
<td>${item.b}</td>
<td>${item.c}</td>
<td>${item.d}</td>
<td>${item.e}</td>
</tr>`;
}
function renderTable(predicate, parentElement, data){
const rows = data
.filter(predicate)
.map(toRowString);
parentElement.innerHTML = `<table>${rows}</table>`;
}
function initialRender(data) {
const stuff = document.getElementById("stuff-info");
const checkbox = document.querySelector("input[name=hide]");
renderTable(filterAll, stuff, data);
checkbox.addEventListener('change', function(event) {
renderTable(
event.target.checked ? filterA : filterAll,
stuff,
data
);
}
}

Why doesn't my innerHTML method work to list things in my contact form?

I am making a program, and I'm wondering why all I see on my html page is the form, but only a single . where the bulleted list for an unordered list should be. I input the user input in the fields, but it doesn't show me the data in the fields, like it's supposed to, when I click submit. Here's the code.
function getFormElements() {
var gather_form_elements = new Array(
$("#first_name").val(),
$("#last_name").val(),
$("email").val(),
$("#phone_number").val()
);
displayValues(gather_form_elements);
}
function displayValues(gather_form_elements) {
for(i=0; i<gather_form_elements.length; i++)
{
document.getElementById("contact_info").innerHTML = "<li>" + gather_form_elements[i] + "</li>";
}
}
Because you are overiding it on every iteration. Try to accumulate the html before using innerHTML like this:
var html = "";
for(var i = 0; i < gather_form_elements.length; i++) {
html += "<li>" + gather_form_elements[i] + "</li>";
// ^^ the += is crucial. If you don't use it, it will just replace the content of html (the innerHTML in your code), we need to use += to append to html instead of overriding it.
}
document.getElementById("contact_info").innerHTML = html;
You can acheive the same result using only one line of code:
document.getElementById("contact_info").innerHTML =
'<li>' + gather_form_elements.join('</li><li>') + '</li>';

Javascript function isn't recognized

I don't have many knowlege in javascript so I don't know what is the problem here,
I create divs dynamically in js and each div call a function when is clicked but the function is not recongized. This is part of the code
for (......) {
var listatema = document.createElement("div");
listatema.innerHTML += "<a href='javascript: void(0)' onClick='functest(" + pag + ")'>" + temat + "</a>";
document.getElementById('menu').appendChild(listatema);}
}
"tema" is a text, the function "functest" has an argument "pag[aux]", this is a number.
The function is:
function functest(arg){
console.log(arg)
}
other alternative that i tried is change that: onClick='"+ functest(pag) +"':
i change the position of Quotation marks "" and the function work good but it is executed when the page is loaded, it don't wait to do click.
Your code should work if you're doing something like:
function functest(arg) {
console.log(arg);
}
for (var i = 0; i < 10; i++) {
var listatema = document.createElement("div");
listatema.innerHTML += "<a href='javascript: void(0)' onClick='functest(" + i + ")'>" + i + "</a>";
document.getElementById('menu').appendChild(listatema);
}
<div id="menu"></div>
I would, however, recommend using addEventListener or setting the onClick handler on the document element object rather than setting the innerHTML. Note that setting innerHTML is not advised, especially when rendering user input. See https://developer.mozilla.org/en-US/docs/Web/API/Element/innerHTML#Security_considerations. In your case, it probably isn't really an issue, but it's good practice to avoid it if you can :)
for (var i = 0; i < 5; i++) {
var wrapper = document.createElement("div");
var listatema = document.createElement("a");
listatema.textContent = i;
listatema.href = "javascript:void(0)";
listatema.addEventListener('click', function(e) {
console.log(this.i);
}.bind({ i : i }));
wrapper.appendChild(listatema);
document.getElementById('menu').appendChild(wrapper);
}
<div id="menu"></div>
onClick='functest(\""+ pag +"\")'
you forgot to quote the parameter.

Regex, doesnt stop matching

I'm working on a bbcode example, but i cannot seem to get it to work.
the regex matches all the [img] tags and make it all look wierd. I'm trying to have the option to click on the image and get it full size and when I do, everything becomes a link (when i have more than once img-tag).
Here's my text:
[img size="small" clickable="no"]img1.jpg[/img]
[img size="large" clickable="yes"]img2.jpg[/img]
Here's my source code:
var bbArray = [/\n/g,
/\[img size="(.*?)" clickable="yes"\](.*?)\[\/img\]/g,
/\[img size="(.*?)" clickable="no"\](.*?)\[\/img\]/g];
var bbReplace = ['<br>',
'<img src="'+path+'img/$1_$2?'+ new Date().getTime() +'" alt="$2">',
'<img src="'+path+'img/$1_$2?'+ new Date().getTime() +'" alt="$2">'];
The operation:
for (var i = 0; i < content_text_bb.length; i++) {
content_text_bb = content_text_bb.replace(bbArray[i], bbReplace[i]);
}
the result:
<img src="localhost/img/small" clickable="no" ]img1.jpg[="" img]
[img size="large_img2.jpg?1423317485160" alt="img2.jpg">;
I'm not that familiar with regex and I really need someone to look at it, I'm lost.
Something that may be of interest to you, Extendible BBCode Parser. An example of use.
var bbcArr = [
'[img size="small" clickable="no"]img1.jpg[/img]',
'[img size="large" clickable="yes"]img2.jpg[/img]'
];
XBBCODE.addTags({
"img": {
openTag: function(params, content) {
params = (params.match(/(\S+?=".*?")/g) || [])
.reduce(function(opts, item) {
var pair = item.match(/(\S+?)="(.*?)"/);
opts[pair[1]] = pair[2];
return opts;
}, {});
var html = '<img src="http://localhost/img/';
if (params.clickable === 'yes') {
html = '<a href="http://localhost/img/' + content +
'" alt="' + content + '">' + html;
}
if (params.size === 'small' || params.size === 'large') {
html += params.size + '/';
}
html += content + '" />';
if (params.clickable === 'yes') {
html += '</a>';
}
return html;
},
closeTag: function(params, content) {
return '';
},
displayContent: false
}
});
bbcArr.forEach(function(item) {
var result = XBBCODE.process({
text: item,
removeMisalignedTags: false,
addInLineBreaks: false
});
this.appendChild(document.createTextNode(result.html + '\n'));
}, document.getElementById('out'));
<script src="https://rawgithub.com/patorjk/Extendible-BBCode-Parser/master/xbbcode.js"></script>
<pre id="out"></pre>
First thing first, your loop should be:
for (var i = 0; i < bbArray.length; i++) {
(not content_text_bb.length)
Secondly, the issue you have is with this size="(.*?). This says: match any content non-greedily till I find the first "thing-that-follow" (in this case the thing-that-follows is the first occurrence of " clickable="yes"
If you look at your input text, the search for [img size="{ANYTHING}" clickable="yes"] means that {ANYTHING} is: small" clickable="no"]img1.jpg[/img][img size="large and you can see how that returns your results, and breaks everything.
So, it should firstly be noted that regexps are not the best tool for language processing (plenty of posts on SO and the internet at large on the topic). In this particular case, you can fix your problem by being very specific about what you want matched.
Do NOT match "anything". If you want to match a size attribute, look for digits only. If you want to match any property value, look for "{ANYTHING_NOT_DOUBLE_QUOTES}". So, with that said, if you change bbArray to the code below, it should work in the particular example you have given us:
var bbArray = [/\n/g,
/\[img size="([^"]*)" clickable="yes"\](.*?)\[\/img\]/g,
/\[img size="([^"]*)" clickable="no"\](.*?)\[\/img\]/g];
Just to be clear: while this should work on your current input, it is by no mean robust bbcode processing. It will only match [img] bbcode tags that have exactly one size attribute and one clickable attribute, in that order!! Most free-to-type bbcode out-there will have much broader variations, and this code obviously won't work on them.

How I find the checkbox by id and apply attribute on string using JQuery?

I have a following html string of contentString:
var content =
'<div id="content">' +
'<div>' +
'<input name="tBox" id="select" type="checkbox" value="" '+
'onclick="changeView()"> Select for operation' +
'<p style="text-align:right">View details</p>' +
'</div>' +
'</div>';
Here, How I find the checkbox select by id and add attribute checked on changeView() function?
function changeView(m) {
//find the select id from content string
var checkbox = content.find($('#select'+m));
// Apply the checked property on checkbox.
checkbox.attr("checked","checked");
}
Thanks in advance.
If you convert it to a JQuery object first then you can do it like this:
var contentObj = $(content);
var checkbox = contentObj.find("#select");
checkbox.attr("checked", true);
then if you need it back at html string:
content = contentObj[0].outerHTML;
Note: If outerHTML is not working as expected, the following JQuery can be used as an alternative:
content = contentObj.clone().wrap('<div>').parent().html();
If m is meant to be the id you want to find (e.g. "select"), then use this:
var checkbox = contentObj.find("#" + m);
Live Example: Here is a working example
Here is the complete function for easy reference:
function changeView(m) {
var contentObj = $(content);
var checkbox = contentObj.find("#" + m);
checkbox.attr("checked", true);
content = contentObj[0].outerHTML;
}
You need to compile the string into a DOM object first by wrapping it in a jQuery call first. Then you can use the find method.
So:
var dom = $(content),
select = dom.find('#select');
In any case, there is no need to add the 'checked' attribute, because when you click the checkbox, it will automatically become checked.
If however, you want to still programmatically check it:
select.on('click', function () {
this.attr('checked', 'checked');
});
Simply like this
function changeView(m) {
//find the select id from content string
var checkbox = content.find('#select');
// Apply the checked property on checkbox.
checkbox.attr("checked","checked");
}
if you want to pass id then
function changeView(m) {
//find the select id from content string
var checkbox = content.find("#" + m);
// Apply the checked property on checkbox.
checkbox.attr("checked","checked");
}
Since you're using the onclick handler, you don't really need to do any of that :
in html : onclick="changeView(this);"
function changeView(box) {
if(box.checked) { stuff; }
// or get jquery ref to that box :
$(box).prop("checked", true);
}

Categories

Resources