JavaScript replace works in console, but not on the website - javascript

I've been trying to create a list of tags and separate them using commas. I'm using Webflow and sadly it's not possible in their cms.
I thought of a workaround where I would replace the commas with code using JavaScript.
Here's the code:
function tags() {
var tag = document.getElementById("tag__wrap").innerHTML;
str = tag.replace(/,/g, '</p><p class="tag__wrap">');
}
tags();
console.log(str);
For some reason the code works fine when I look it up in the console, but doesn't actually show anything on the actual website.
Any thoughts?

If your goal is to create multiple elements in place of the single element (that has the comma separated tags), then you need to manipulate the DOM. It is not enough to assign HTML to a string variable.
There are many ways to do this. Here is one:
function tags() {
var elem = document.getElementById("tag__wrap");
var tags = elem.textContent.match(/[^,\s]+/g) || [];
elem.textContent = tags.shift();
for (let text of tags) {
var newElem = elem.cloneNode();
newElem.textContent = text;
elem.insertAdjacentElement("afterend", newElem);
elem = newElem;
}
}
// Delay the change, so you can see before & after in the snippet
setTimeout(tags, 1000);
#tag__wrap {
background: lightblue;
display: inline-block;
margin: 5px;
padding: 3px 8px;
border-radius: 4px;
}
<p id="tag__wrap">algorithm,javascript,html,css</p>

Related

How do I restrict the style of the text pasted in a contenteditable area?

I came across this Stack Overflow post where was discussed the exact thing I needed: to be able to paste text into a contenteditable area, retaining only a few styles. I ran the code snippet there and it work fine. However, when I tried it on my page, all styles were being removed, including those I wanted to keep, like bold and italic. After comparing the codes and a few experimentations, I realized that the reason it was not working was because I was using external CSS, instead of inline.
Is there any way I can make it work with external CSS? I will never know the origin of the text users will post in that contenteditable, and how was style applied to it, so I am looking to address all possibilities.
Also, is there a way to make it work with dragged and dropped text, instead of just pasted text? I tried replacing the event it is listening to from "paste" to "drop", but I get the error e.clipboardData is undefined
const el = document.querySelector('p');
el.addEventListener('paste', (e) => {
// Get user's pasted data
let data = e.clipboardData.getData('text/html') ||
e.clipboardData.getData('text/plain');
// Filter out everything except simple text and allowable HTML elements
let regex = /<(?!(\/\s*)?(b|i|em|strong|u)[>,\s])([^>])*>/g;
data = data.replace(regex, '');
// Insert the filtered content
document.execCommand('insertHTML', false, data);
// Prevent the standard paste behavior
e.preventDefault();
});
.editable {
width: 100%;
min-height: 20px;
font-size: 14px;
color: black;
font-family: arial;
line-height: 1.5;
border: solid 1px black;
margin-bottom: 30px;
}
.big {
font-size: 20px;
}
.red {
color: red;
}
.bold {
font-weight: bold;
}
.italic {
text-decoration: italic;
}
<p class="editable" contenteditable></p>
<p class="notEditable">
Try pasting this paragraph into the contenteditable paragraph above. This text includes <b>BOLD</b>, <i>ITALIC</i>, <s>STRIKE</s>, <u>UNDERLINE</u>, a <a href='#'>LINK</a>, and <span style="font-size:30px; color:red; font-family:Times New Roman">a few other styles.</span> All styles are inline, and it works as expected.
</p>
<p>Now, try pasting this paragraph with external styles. <span class="big">Big</span > <span class="red">red</span> <span class="bold">bold</span> <span class="italic">italic</span>. It no longer works.</p>
As other answer pointed out I don't know any way of getting CSS styles out of other pages using clipboard. . But at your own you could do something like this:
Get getComputedStyle (CSS only) of all elements filter out wanted style, in this example fontStyle and fontWeight. Then you can condition if fontStyle==="italic" or fontweight==="700" (bold), textDecoration==="underline rgb(0, 0, 0)" and wrap that elements into its HTML tags.
You do this because your regex function is only targeting tags, not even inline CSS property font-style: italic;. Witch is a shame, it would make things a bit easier as you could just read every elements CSS class style and apply it inline, but this way you need to condition it and apply HTML tag.
if ( style.fontStyle==="italic"){
element.innerHTML = "<i>" + element.innerHTML + "</i>";
;}
if ( style.fontWeight==="700"){
element.innerHTML = "<b>" + element.innerHTML + "</b>";
;}
if (style.textDecoration==="underline rgb(0, 0, 0)"){
element.innerHTML = "<u>" + element.innerHTML + "</u>";
;}
In example below if you copy Now, try pasting this paragraph with external styles. Big red bold italic. It no longer works. you will get bold,underline and italic. You can do the same for rest of your filtering options.
const el = document.querySelector('p');
el.addEventListener('paste', (e) => {
// Get user's pasted data
let data = e.clipboardData.getData('text/html') ||
e.clipboardData.getData('text/plain');
//console.log(data)
// Filter out everything except simple text and allowable HTML elements
let regex = /<(?!(\/\s*)?(b|i|em|strong|u)[>,\s])([^>])*>/g;
data = data.replace(regex, '');
//console.log(data)
// Insert the filtered content
document.execCommand('insertHTML', false, data);
// Prevent the standard paste behavior
e.preventDefault();
});
[...document.querySelectorAll('body *')].forEach(element=>{
const style = getComputedStyle(element)
if ( style.fontStyle==="italic"){
element.innerHTML = "<i>" + element.innerHTML + "</i>";
;}
if ( style.fontWeight==="700"){
element.innerHTML = "<b>" + element.innerHTML + "</b>";
;}
if (style.textDecoration==="underline rgb(0, 0, 0)"){
element.innerHTML = "<u>" + element.innerHTML + "</u>";
;}
});
.editable {
width: 100%;
min-height: 20px;
font-size: 14px;
color: black;
font-family: arial;
line-height: 1.5;
border: solid 1px black;
margin-bottom: 30px;
}
.big {
font-size: 20px;
}
.red {
color: red;
}
.bold {
font-weight: bold;
}
.underline{
text-decoration: underline;
}
.italic {
font-style: italic;
}
<p class="editable" contenteditable></p>
<p class="notEditable">
Try pasting this paragraph into the contenteditable paragraph above. This text includes <b>BOLD</b>, <i>ITALIC</i>, <s>STRIKE</s>, <u>UNDERLINE</u>, a <a href='#'>LINK</a>, and <span style="font-size:30px; color:red; font-family:Times New Roman">a few other styles.</span> All styles are inline, and it works as expected.
</p>
<p id="container"><span class="underline">Now</span>, try pasting this paragraph with external styles. <span class="big">Big</span > <span class="red">red</span> <span class="bold" >bold</span> <span class="italic">italic</span>. It no longer works.</p>
Unfortunately, there is no way to keep the properties of a class from an external source. If you would print the content of the clipboard, you will see that you receive the raw HTML content as it is on the external page, for example:
<div class="some-class">this is the text</div>
The class properties would not be inlined by the browser! And as the content is from an external source, you have no power over it.
On the other hand, if the content is from your page (so the class is defined), you could parse the received HTML and filter the CSS properties, keeping only what you want. Here you have a code sample using vanilla Javascript, no libraries required (also available on Codepen):
const targetEditable = document.querySelector('p');
targetEditable.addEventListener('paste', (event) => {
let data = event.clipboardData.getData('text/html') ||
event.clipboardData.getData('text/plain');
// Filter the string using your already existing rules
// But allow <p> and <div>
let regex = /<(?!(\/\s*)?(div|b|i|em|strong|u|p)[>,\s])([^>])*>/g;
data = data.replace(regex, '');
const newElement = createElementFromHTMLString(data);
const cssContent = generateFilteredCSS(newElement);
addCssToDocument(cssContent);
document.execCommand('insertHTML', false, newElement.innerHTML);
event.preventDefault();
});
// Scan the HTML elements recursively and generate CSS classes containing only the allowed properties
function generateFilteredCSS(node) {
const newClassName = randomString(5);
let content = `.${newClassName}{\n`;
if (node.className !== undefined && node.className !== '') {
// Get an element that has the class
const elemOfClass = document.getElementsByClassName(node.className)[0];
// Get the computed style properties
const styles = window.getComputedStyle(elemOfClass);
// Properties whitelist, keep only those
const propertiesToKeep = ['font-weight'];
for (const property of propertiesToKeep) {
content += `${property}: ${styles.getPropertyValue(property)};\n`;
}
}
content += '}\n';
node.className = newClassName;
for (const child of node.childNodes) {
content += generateFilteredCSS(child);
}
return content;
}
function createElementFromHTMLString(htmlString) {
var div = document.createElement('div');
div.innerHTML = htmlString.trim();
return div;
}
function addCssToDocument(cssContent) {
var element = document.createElement("style");
element.innerHTML = cssContent;
var header = document.getElementsByTagName("HEAD")[0];
header.appendChild(element);
}
function randomString(length) {
var result = '';
var characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
var charactersLength = characters.length;
for (var i = 0; i < length; i++) {
result += characters.charAt(Math.floor(Math.random() * charactersLength));
}
return result;
}
.editable {
width: 100%;
min-height: 20px;
font-size: 14px;
color: black;
font-family: arial;
line-height: 1.5;
border: solid 1px black;
margin-bottom: 30px;
}
.red-bg {
background-color: red;
font-weight: bold;
}
<p class="editable" contenteditable></p>
<p class="red-bg test">
This is some text
</p>
About the drag and drop functionality, you have to use event.dataTransfer.getData() in the drop event listener, the rest is the same.
References
How to generate a DOM element from a HTML string
How to add CSS classes at runtime using Javascript
How to generate a random string (unique ID) in Javascript
Drag&drop data transfer
You could accomplish what you want, but would require an additional set of considerations.
First you add an "Add Source Assets" button, which the user would supply the source page URL... Then you would fetch the source HTML and match the pasted content, with the source content, then interrogate the source elements for all related attributes, referenced classes etc.
You could even process the source URL's markup to extract it's css and images as references... Essentially onboarding the resources based on a whitelist of acceptable media and styles.
Depending on your needs and DOM Kung Fu, you could extract all "consumed" css and import those styles... It really depends how far you want to go.

How to use html tag inside a java script?

I want to display elements like this I'm creating an array and pushing some numbers into that array using javascript. After pushing a number I'm trying to use a html tag which displays that number inside a circle. But I'm facing trouble while doing that.
This is my javascript for pushing elements into array:
data.push(5);
data.push(6);
data.push(4);
Now the array will consists of the elements 5,6,4. Now I want to display these elements using some html tag.
The html tag I'm using to display these elements are:
<span class="w3-badge">5</span>
<span class="w3-badge">6</span>
<span class="w3-badge">4</span>
Then these elements will display in a circle. This is static because I'm giving numbers in the html tags.Actually I want to load these numbers using javascript.
I'm trying in this way,but it is not working:
"<span class="w3-badge">"data.push(5);"</span>"
"<span class="w3-badge">"data.push(6);"</span>"
"<span class="w3-badge">"data.push(5);"</span>"
Can Someone help me with this???
Are you filling this HTML with javascript?
"<span class=\"w3-badge\">" + data.push(5) + "</span>"
You will need to use javascript to find the DOM elements and populate them. You will get to get the elements by class name and then loop through them to reassign the inner html.
var doms = getElementsByClassName( 'w3-badge' );
for( var i = 0; i < doms.length; i++ ) {
doms[ i ].innerHTML = data[ i ];
}
Why don't you simply add those numbers to the array if you say it's something static, and then parse the array with a for loop to add the HTML tags? You need to correctly escape using the right quotes and concatenate:
var test = [];
var data = [5, 6, 4];
for(var i = 0; i < data.length; i++){
test += "<span class='w3-badge'>" + data[i] + "</span>";
}
document.getElementById('container').innerHTML = test;
<div id='container'>
</div>
as you've tagged jquery, i will show you a jQuery way:
you must have a parent element, assuming it's a div with id "numbers", you could just:
var numbers = $('#numbers');
for(var i = 0; i < data.length; i++) {
numbers.append($('<span class="w3-badge">'+data[i]+'</span>'));
}
With data.push(num) you push num to the next free index in the array.
With data[index] you get the value (num) at given index.
Next point:
You can't use unescaped double quotationmarks inside double quotationmarks.
Use single questionmarks instead of one of them.
And you have to use a + to connect two strings.
I think, you don't got the fundamental basics of Javascript.
You should work trough something like this before you start fiddling around: http://jqfundamentals.com/chapter/javascript-basics (~ 5-15 min reading)
You can either use javaScript
var test = "<span class='w3-badge'>"+data.push(5)+"</span>";
or the ES6 attempt, this will work inline
var test = `<span class='w3-badge'>${data.push(5)}</span>`;
To insert a DOM described as above into the HTML first do:
var span1 = document.createElement("span");
span1.className = "w3-badge";
span1.innerHTML = 5; //create a span as above and set 5 between it
document.body.append(span1); //add completed span obj to document body
<body></body>
You can also create a string s with the html code you want, and call
document.body.append(s);
to add the span to the body of the document
there are a lot of ways to get the values of DOMS as well.
most of them begin with document.getElementsBy and can be found here
each of these returns an array of all of the html doms that match your query.
document.getElementById() in particular will return one element only, since only one element should have a given id in the html.
you can then call .innerHTML to a particular DOM selected with any of those methods
so in yours
var data = [];
var spans = document.getElementsByClassName("w3-badge");
for( var i =0; i < spans.length; i++){
data.push(spans[i].innerHTML);
}
console.log(data);
<span class="w3-badge">5</span>
<span class="w3-badge">6</span>
<span class="w3-badge">4</span>
As you have tagged JQuery, here is a clean version which loops over every element with the class w3-badge:
var data = [];
data.push(5);
data.push(6);
data.push(4);
$('.w3-badge').each(function(i, obj) {
document.getElementById(i).innerHTML = data[i];
});
JSFiddle Demo
Snippet (with CSS Circle):
var data = [];
data.push(5);
data.push(6);
data.push(4);
$('.w3-badge').each(function(i, obj) {
document.getElementById(i).innerHTML = data[i];
});
.cl {
border-radius: 50%;
behavior: url(PIE.htc);
width: 36px; height: 36px;
padding: 8px;
background: #fff;
border: 2px solid #666;
color: #666;
text-align: center;
font: 32px Arial, sans-serif;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<span id="0" class="cl w3-badge">0</span>
<span id="1" class="cl w3-badge">0</span>
<span id="2" class="cl w3-badge">0</span>
Pure JavaScript version:
Note: You have to give every element an ID beginning from zero.
This isn't a that beautiful version but it works:
var data = [];
data.push(5);
data.push(6);
data.push(4);
for (i = 0; i < data.length; i++){
console.log(i + " = " + data[i]);
document.getElementById(i).innerHTML = data[i];
}
Demo:
var data = [];
data.push(5);
data.push(6);
data.push(4);
for (i = 0; i < data.length; i++){
console.log(i + " = " + data[i]);
document.getElementById(i).innerHTML = data[i];
}
.cl {
border-radius: 50%;
behavior: url(PIE.htc);
width: 36px; height: 36px;
padding: 8px;
background: #fff;
border: 2px solid #666;
color: #666;
text-align: center;
font: 32px Arial, sans-serif;
}
<span id="0" class="cl w3-badge">0</span>
<span id="1" class="cl w3-badge">0</span>
<span id="2" class="cl w3-badge">0</span>
JSFiddle Demo

Javascript add margins between each object in an array

I'm new to Javascript and I'm building a connect four game to learn about javascript. The problem is that I am unable to add margins between the 'o' so they all look clumped together.
I would like help adding margins or padding between the 'o's. Some thoughts are that I may need to add each row of 'o' in a table. However, I'm updating it through the javascript function. How do I get around that?
My javascript file
var first_row = ['o','o','o','o','o','o','o'];
onload = function ()
{
document.getElementById("row1").innerHTML = first_row;
}
HTML file
<h1 class="space" id="row1"></h1>
In pure javascript (not using jquery) with a for in loop than append spans with the '0's for styling.
var first_row = ['o','o','o','o','o','o','o'];
var row = document.getElementById('row1');
for (var i in first_row) {
var span = document.createElement('span');
span.innerHTML = first_row[i];
row.appendChild(span);
}
See here: http://jsfiddle.net/95JqK/17/
You'll have to encapsulate them into tags and put css rules on them.
A simple solution would be:
var first_row = ['<span>o</span>','<span>o</span>','<span>o</span>','<span>o</span>','<span>o</span>','<span>o</span>','<span>o</span>'];
onload = function ()
{
document.getElementById("row1").innerHTML = first_row;
}
And in a css file:
span {
margin: 0 10px 0 10px;
}
Perhaps you could use CSS on the class="space" elements
.space{
padding: 8px;
}

Add css before appending child

So I have a div (with the id of "thecolor2") that I want to append to an unordered list, but before I append it, I want to set its background color to a variable which has the value of a hex code. However, for some reason, it doesn't take in the color.
Here is the CSS:
#thecolor2{
height: 50px;
width: 50px;
border-radius: 100px;
border: 1px solid yellow;
position: relative;
bottom: 635px;
}
Her is the HTML:
<ul id = "allposts"></ul>
And here is the JS:
var thestream = document.getElementById('allposts');
var oneofpost = document.createElement('li');
var thecolor2 = document.createElement('div');
thecolor2.id = "thecolor2";
$("#thecolor2").css("background-color", color);
thestream.appendChild(oneofpost);
thestream.appendChild(thecolor2);
You cant use a jQuery ID selector to match a node which hasn't been added to the document tree. You can simply use plain DOM to set its inline CSS style like this:
thecolor2.style.backgroundColor = color
As described by Carlo in another answer, you cannot use the jQuery selector to select elements that haven't been added. You can however, turn a created DOM element into a jQuery object by doing:
var thecolor2 = $(document.createElement('div'));
However, if you're going to be using jQuery then I suggest writing everything in jQuery, otherwise stick with using pure JavaScript for everything.
jQuery
var thestream = $('#allposts');
var oneofpost = $('<li></li>');
var thecolor2 = $('<div></div>');
thecolor2.prop('id', "thecolor2")
.css({
backgroundColor: color
}).appendTo(oneofpost);
thestream.append(oneofpost);
See jsFiddle
JavaScript
var thestream = document.getElementById('allposts');
var oneofpost = document.createElement('li');
var thecolor2 = document.createElement('div');
thecolor2.id = "thecolor2";
thecolor2.style.backgroundColor = color;
oneofpost.appendChild(thecolor2);
thestream.appendChild(oneofpost);
See jsFiddle
Also I'm assuming you're trying to append a list item to the ul, so I corrected the code you had there with appendChild.

How to iterate through all textarea tags and add a node after them?

I'm not a jquery developer and I'm not much of a javascript developer too. I'm trying a quick hack that makes my life simple. This is the page:
What I'm trying to accomplish is:
I want to add a div of mathjax_preview class after each textarea element on the page.
The inner.HTML of that div must the text inside the corresponding textarea
But it doesn't seem to work.
I've the following javascript code in js/1.js and it is being loaded when the page loads:
var preview_number = 0;
$("textarea").each(function() {
var d = $(this).next();
if (!d.hasClass("mathjax_preview")) {
preview_number++;
var d = $("<div class='mathjax_preview' " +
"style='padding: 5px; color: black; background: #eee; border: 1px solid #888;float:left;'" +
"></div>");
d.attr("id", "mathjax_preview_" + preview_number);
d.insertAfter(this);
}
d.text($(this).val());
MathJax.Hub.Queue([ "Typeset", MathJax.Hub, d.attr("id") ]);
});
EDIT:
I just inserted following snippet in the beginning of the above file:
var preview_number = 0;
var elements = document.getElementsByTagName("textarea");
alert(elements.length);
Its alerting 0. How come?
Try the following (I've not tested this as it's straight off the top of my head):
$(document).ready(function() {
var preview_number = 0;
$("textarea").each(function() {
preview_number++;
var d = document.createElement("div");
$(d).attr("id", "mathjax_preview_" + preview_number);
$(d).addClass("mathjax_preview");
$(d).css("padding", "5px");
$(d).css("color", "black");
$(d).css("background", "#eee");
$(d).css("border", "1px solid #888");
$(d).css("float", "left");
$(d).html($(this).val());
MathJax.Hub.Queue([ "Typeset", MathJax.Hub, $(d).attr("id") ]);
$(this).append(d);
});
});
The variable textarea does not exist. Use this instead:
d.insertAfter(this);
You have no textarea variable defined.
d.insertAfter(textarea);
I beleive you can use $(this) reference.
d.insertAfter($(this));
What is textarea in d.insertAfter(textarea);?
It seems to be d.insertAfter($(this));.
The alert message is showing you zero because you have not put your javascript file at the end of page nor you have used ready callback.
To use ready callback add the code as follows
$(document).ready(function() {/*add your javascript code here */});

Categories