I have this HTML:
<span>Lies Hendriks</span>
How can I select the second word with CSS. I can not change the HTML of this document.
I want give "Hendriks" another style. Or can I do it with JavaScript?
You will need to split up into seperate entities to apply different styles to each word.
<span style="style1">Lies</span><span style="style2">Hendriks</span>
There is no way to selectively apply styles to a single word using JS or CSS without changing the HTML itself.
You can't do with CSS, but if you know your structure (for instance, that it's always the second word you need to style differently) you can do it with JavaScript (assuming that you can inject your JS into the page, of course).
This is an example that may be a bit convoluted, but if your HTML has:
<span>Lies Hendriks</span>
then you can do it like so (upon your DOM loading event):
var span1 = document.getElementById('span1');
span1.innerHTML = span1.innerHTML.replace(/(\s+)(.*)/, '$1<span class="red">$2</span>');
assuming you have
.red
{
color: red;
}
in your CSS
The above code allows me to make everything after the first word red. That should give you an idea how to do that, depending on your structure.
Related
I have user-supplied (arbitrary) css that is delivered as a single text string. This string can be stuffed into a style tag, and I can get a DOM element back from that if need be.
This css should only apply to part of the entire document, which I'll be wrapping in a div with a unique id. Given a rule that looks like this
.userclass > .whatever {
background: blue;
}
... I'd like to be able to add #uniqueid in front of each rule so that it looks like
#uniqueid .userclass > .whatever {
background: blue;
}
This seems non-trivial. I don't believe it's something I can manage in with a clever regex. Are there any methods in javascript that make this easy, or at least manageable?
If the css is put into a style tag, then it becomes possible to access each rule individually via index with .cssRules[n], but even then each rule can target multiple selectors. Is it as simple as a regex substitution there, looking for commas in between that and the first open curly bracket? Is there more to it than this?
Consider that any arbitrary (valid) css might be supplied.
I would recommend to send this file to sass preprocessor on backend. Then you can import that css (as scss) and do #uniqueid { #import 'user-styles.scss' }. JS string manipulation on frontend will be probably far from bulletproof..
A simple aproach:
If you get the style tag as a string and split it at } and add your #uniqueid at the beginning of each array element. Subsequently you just have to loop through the array and convert it back to a single string and append it to the html.
I don't know if you have jQuery but a solution in jQuery would look like that.
This works for every CSS-Code. You can beautify the output if wanted with some additional tweaking.
var style = $('style').text();
style = style.split('}');
var newStyle = '';
$.each(style,function(i,element){
newStyle = newStyle + ' #uniqueid '+element+' } ';
})
$('style').text(newStyle);
Since its a one time affair I'd personally just do it quick-and-dirty...
https://www.sassmeister.com/
#uniqueid {
// paste all your css
}
If this is going to be more regular you definitely want to implement a preprocessor in you build process.
So I use PDF.js to render pdf to html. On top there is a text layer.
What I want to implement is that when you click on a sentence there will be a class added to this sentence.And I want to do this in Angular 4 Component.
I have stumbled upon a problem here because the pdf is rendered to html by lines(every line is in a different div).
Example of pdf in html:
<div style="left: 86.0208px; top: 481.589px; font-size: 8.03709px; font-
family: serif; transform: scaleX(1.00581);">
timestamp server to generate computational proof of the chronological
order of transactions. The
</div>
<div style="left: 86.0208px; top: 490.899px; font-size: 8.03709px; font-
family: serif; transform: scaleX(0.9335);">
system is secure as long
as honest nodes collectively control more CPU
power than any
</div>
Any idea how should I implement this functionality?
Main goal is to highlight the exact sentence what is clicked and doing it by
manipulating html.
Here's an approach that highly depends on how your html looks like and for which part you want to implement the sentence highlighting. If it's just one text block with multiple lines on top of the page and nothing more, I'd say that you can replace the whole block with an updated HTML block, maybe even a single <p>.
combining all to one big string
You should find this part in the HTML created by PDF.js, iterate over all the child divs and combine every text part of it to one big string by adding it up all together, just string concatenation. One problem might be the access of the child divs. If the HTML is rendered by an angular application, you can reference DOM elements by giving them attribute names like #textBlock. Then you can access those elements with #ViewChild which brings some fancy functions with it to walk down an elements subtree like childNodes and data. This may be helpful to extract the text and concatenate the string.
split the text into sentences
Next thing to do is split this big text block string into sentences. Having fixed punctuation marks like . ! ? we can use someting like a regular expression to split it on the right spot. The string function replace in combination with a regular expression should do the job here. As a result we want to have an array of sentences. The regex may look something likes this, also I'm not 100% if it works, because I just found it in this answer:
var bigTextBlock="Big text block. No more divs. Only a string";
var sentences = bigTextBlock.match( /[^\.!\?]+[\.!\?]+/g );
remove the current divs
Now that's not too bad for a start. We now want to remove the current divs and create new html tags. There are multiple ways to do this. In both cases we might need to have a reference to the parent div of the text block divs from before, that we probably already have.
First option is to set something like [innerHTML]. This removes the old divs and creates new ones, but gets tricky when you want to implement an onclick action, because this way we bypass angular.
The other way is to manipulate its children through your reference element. For this we can use a so called Renderer2 that is injected as a service. You can do different stuff with it like creating new tags, removing children and also creating onClick listeners on nodes, which is what we probably need to do anyway. For now we only want to remove the old childNodes.
create adjusted html
As we now have every sentence isolated, we can create one big <p>div that contains a <span> div for every sentence that we have. This way we can give the span just another css class if the user clicks inside of this text part and therefore having a highlight for every sentence. As stated before the html could be placed through [innerHTML] or by creating them as children of our reference. In both cases we need to use Renderer2 to make the <span> listen to an onclick action. Here's some code that combines the span creation and adding the listener both through Renderer2.
#ViewChild('textBlock') textBlock: ElementRef;
constructor(private renderer: Renderer2, private router: Router) { }
createSpans(sentences: string[]){
sentences.forEach(sentence=>{
// create elements
const span = this.renderer.createElement('span');
const spanText = this.renderer.createText(sentence);
// append the sentence to the span div
this.renderer.appendChild(span, spanText);
// append the span div to the parent
this.renderer.appendChild(this.textBlock.nativeElement, span);
// listen to the onClick
this.renderer.listen(span, 'click', (event) => {
// set a highlight class
span.class.highlighted = true;
});
});
}
I know this is a lot to do and it gets tricky at some parts, but this is probably how I would handle it. But again it depends highly on how your HTML currently looks like and how you want it to look like after the changes.
I am making a chrome extension, and I want to find all occurrences of a certain word, and put them inside a new div class so I can change them with CSS.
Right now my content.js file looks like this:
(function() {
function change() {
var words = new Array("color");
var html = document.body.innerHTML;
for(var i = 0; i < words.length; i++) {
var reg = new RegExp(words[i], 'ig');
var html = html.replace(reg, '<div class="colorClass">'+words[i]+'</div>');
}
document.body.innerHTML = html;
}
change();
})();
But when I do that, entire web pages get messed up and become unrecognizable (for example, the "Color" wikipedia page).
When I try something simpler like:
var html = html.replace(reg, '<i>'+words[i]+'</i>');
It works just fine. Why is this whole page getting messed up when I try to add divs?
How can I fix this?
One reason the styles are messed up are because the native web page's CSS is overriding the styles loaded from your content script.
The second reason is that you need to be very careful when modifying HTML. You need be careful only to modify text. The current method you are using - you are possibly modifying html classes, ids, etc.
I've actually built an extension (Source code for reference) that does something very similar. I'll share my learnings:
JS
I had used NodeIterator which is fairly unknown. I discovered this API a year ago from another extension: https://code.google.com/p/chrome-type-ahead/
NodeIterator will help you filter out the true text elements that you want to add styles to. This will help you avoid modifying any valid html(classes, ids, etc.) that matches your regex.
CSS
I would suggest adding a class to the html tag in addition to the html you are inserting. The reason for this is that you'll need to be very specific in your styles.
You want to avoid at all cost any chance of a collision with a selector of an existing page.
In addition you need to make sure to add your own css resets that are not only namespaced but also have !important on each attribute.
You CANNOT assume anything about the html tag that you are inserting. Sites can have very generic selectors that will completely mess up your styles. And also the sites can have very specific selectors (using id) that are more specific than you're styles (hence the need for important)
html.very-specific-class .very-specific-color-class {
/*
padding, margin, border, etc.
difficult to add all properties but add all the common ones at the very least
*/
padding: 0px !important;
margin: 0px !important;
border: 0px !important;
...
}
This is happening because a <div> is a block-level element, and it pushes other elements to a new line.
<i> however, is an inline element, so it will not change the page layout at all.
I would recommend using a <span>,instead of a <div>, as span's have no default styling in browsers.
Because divs are block elements. You can use spans, which are inline elements:
var html = html.replace(reg, '<span class="colorClass">'+words[i]+'</span>');
If you want use div,you can add some css styles,like this:
.colorClass{display:inline-block;}
I have a parent div gal1, inside which there can be further divs and content, but only one img element without an id as you can see below. Now I want to use only Javascript (no jQuery) to change the style of this image, using gal1 as argument (because the rest of the structure inside this div may vary, only this one image will always be there somewhere). I couldn't find any other stackoverflow question that addresses exactly my situation.
<div class="gallery-preview" id="gal1">
<div class="gallery-preview-img" id="gal-img1">
<img src="galleries/kevin-carls/Monks.jpg">
</div>
<div class="gallery-preview-text" id="gal-text1">
<span class="gallery-name" href="">Road to India</span><br/>
<span class="artist-name" href="">Kevin Carls</span>
</div>
</div>
Than you can make use of method called getElementsByTagName('img') than you should get image and update it.
document.getElementById(gal1).getElementsByTagName('img');
get the content by using id, and then query images by using getElementsByTagName
function getImages(contentId) {
var content = document.getElementById(contentId);
// only one image, just return an item; or you can return an array
if (content) return document.getElementsByTagName('img')[0];
}
You can insert CSS which may be more efficient if you have to do this in more than this single case.
http://jsfiddle.net/65Ggv/
var style_rules = [];
style_rules.push("#gal1 img { border: 3px solid green; } ");
var style = style_rules.join("\n");
var el=document.createElement("style");
el.appendChild(document.createTextNode(style));
el.type="text/css";
document.head.appendChild(el);
Unless you absolutely need to pick the colors or border sizes dynamically, which I doubt because you are an admitted beginner, stuffing stylesheets in with Javascript is a Rube Goldberg device. It seems nifty to be able to do this, but if your application is important to you, you will regret it. (You might as well use innerHTML to stuff in a stylesheet in that case -- at least it will be faster than making DOM calls.)
Pranay Rana's answer to use getElementsByTagName is the best option if your constraints are actually stable (only one img). Obtain an element reference el, to gal1, using getElementById, then var myimg = el.getElementsByTagName("img") and you are almost done.
If you insist upon funking with the style nodes, you can stuff whatever properties you want into the style property of myimg. It becomes inline style.
Even so, you almost certainly do not need to stuff in novel rules, and changing inline style is often avoidable. It is more predictable and stable to modify the class attribute on myimg, and use a predefined set of style classes for the cases you need to handle. This will give a nice clean separation of the style from the script, and avoid both the in-lining of the style rules and run-time mutilation of the style tree by code injection.
We've got a little tool that I built where you can edit a jQuery template in one field and JSON data in another and then hit a button to see the results immediately within the browser.
I really need to expand this though so the designer can edit a full CSS stylesheet within another field and when we render the template, it will have the CSS applied to it. The idea being that once we've got good results we can take the contents of these three fields, put them in files and use them in our project.
I found the jQuery.cssRule plugin but it looks like it's basically abandoned (all the links go nowhere and there's been no development in three years). Is there something better or is it the only game in town?
Note: We're looking for something where someone types traditional CSS stylesheet data in here and that is used immediately for rendering within the page and that can be edited and changed at will with the old rules going away and new ones used in their stead. I'm not looking for something where the designer has to learn jQuery syntax and enter in individual .css("attribute", "value") type calls to jQuery.
Sure, just append a style tag to the head:
$("head").append("<style>p { color: blue; }</style>");
See it in action here.
You can replace the text in a dynamically added style tag using something like this:
$("head").append("<style id='dynamicStylesheet'></style>");
$("#dynamicStylesheet").text(newStyleTextGoesHere);
See this in action here.
The cleanest way to achieve this is by sandboxing your user-generated content into an <iframe>. This way, changes to the CSS won't affect the editor. (For example, input { display:none; } can't break your page.)
Just render out your HTML (including the CSS in the document's <head>, and write it into the <iframe>.
Example:
<iframe id="preview" src="about:blank">
var i = $('#preview')[0];
var doc = i.contentWindow || i.contentDocument;
if (doc.document) doc = doc.document;
doc.open('text/html',true);
doc.write('<!DOCTYPE html><html>...</html>');
doc.close();
If the user should be able to edit a whole stylesheet, not only single style attributes, then you can store the entered stylesheet in a temporary file and load it into your html document using
$('head').append('<link rel="stylesheet" href="temp.css" type="text/css" />');
sounds like you want to write an interpreter for the css? if it is entered by hand in text, then using it later would be as simple as copy and pasting it into a css file.
so if you have a textarea on your page to type in css and want to apply those rules when you press the button, you could use something like this (only pseudocode, needs work):
//for each css id in the text area
$.each($('textarea[name=cssTextArea]').html().split('#'), function({
//now get each property
$.each($(this).split(';'), function(){
$(elem).css({property:value});
});
});
then you could write something to go through each element that your designer typed in, and get the current css rules for it (including those that you applied using some code like the snippet above) and create a css string from that which could then be output or saved in a db. It's a pain and much faffing around with substrings but unfortunately I don't know of a faster or more efficient way.
Hope this atleast gives you some ideas