Mark.js keep marking permanently? - javascript

i am currently using mark.js for a google chrome extension to highlight some text on a web page.
Now whenever i refresh the page or do a right click all my marking is lost.
Is there a way to keep that marking? As far as i see mark.js does not change the DOM.
I would like to mark the text but also be able e.g. when i print that page or save it as a pdf, to maintain the highlighting i did..
That is the way i mark. it is pretty much the same as instructed on https://markjs.io/:
function mark(text, label){
var paragraphs = Array.from(document.getElementsByClassName('description-line'));
switch(label){
case "strAdv":
for(var elem of paragraphs) { // loop through each element in paragraphs array
var instance=new Mark(elem);
instance.mark(text, {className: "strAdv",separateWordSearch:false});
}
break;
....
strAdv.css:
/*CSS to define diferent marking classes*/
.strAdv {
padding: 0;
background-color: chartreuse;
}

Found a way to overcome that problem.
Just store the text items you want to mark and mark them again every time a page gets refreshed.

Related

How to change add and remove tags with JS in django template

I have a quiz Django app which consists of two parts. One is showing 10 sentences with their audio to remember, one per page, and the second is asking questions for the same set of sentences. First part was set up with js function which creates pagination in my html the following way:
my_template.html
<button id="prev">prev</button>
<button id="next">next</button>
<ul class="list-articles" id="dd">
</ul>
<script>
var my_objects = `{{ my_objects|safe}}:` #list of items from my view function
function paginate(action) {
console.log(action)
if (action ==='next') {
page_number++;
}
else{
page_number--;
}
const audio = document.createElement('audio');
audio.src =`/media/${my_objects[page_number].fields.audio}`;
$('#dd').empty();
$('#dd').append('<li><h1>'+ my_objects[page_number].fields['content'] +'</h1></li>');
$('#dd').append(audio);
$('#page_number').val(page_number);
}
document.addEventListener('DOMContentLoaded', function() {
document.querySelector('#next').onclick = function() {
paginate('next'); #same goes for 'prev' button.
}
})
</script>
Now when the user paginated through all the sentences I want to show the continue button and if the user clicks it, start the second part. The second part has absolutely same page except I need to hide content of my objects and leave only audio or vice versa and add textarea tag for the user's input. After that I need to loop over my objects again - now in the form of questions. I need to do that without page re-rendering so I don't need to store the list of objects or query the DB again.
I tried to make it with tag disabling and activating the second loop after the first ends but that looks quite messy and I suppose there is a smarter way of doing that. I'm not a JS developer so any help would be appreciated!
Thanks for all who viewed and not commented! I found the answer myself. Just need to add JS functions which will empty the main tag and refill with necessary field.
Details: By accessing elements of DOM in the following way you can empty any element on a web page. For example, if we have a div like:
<div class= id="maindiv">
<h2> Hello, world! </h2>
</div>
We can empty and refill it with a new header:
var container = document.getElementById("maindiv");
container.empty();
new_header = document.createElement('h2');
new_header.innerHTML = 'Hello, brave new world!'
container.append(new_header);
Same applies to the whole web-page. The only thing is, if it is too big, you probably going to be tired of manipulating the DOM each time. So, you may want to check out some frameworks like React to make it easier .

How to Show/Hide element on specifict url/endpoint

Can we make a script that allows to hide certain elements when endpoints are loaded in our website URL???
For example, when someone clicks on a currency option, it creates an endpoint "/?wmc-currency=USD". Or if implemented into the web like this https://www.yoursite.com/product/?wmc-currency=USD.
I want a div or any element to be hidden when "/?wmc-currency=USD" is added to the url.
Any help would be very valuable to me. Thanks very much
function checkEndpoint() {
if(window.location.search=="?wmc-currency=USD") {
document.getElementById('testid').style.width = 0;
}
}
checkEndpoint();
// you can also delete the div if you want but with this, you can always bring
// it back without having to remake it
make sure you change the id for the div you want
This is the code I ended up using, because style.width doesn't hide the text.... So I use display none
function checkEndpoint() {
if(window.location.search=="?wmc-currency=USD") {
document.getElementById('testid')style.display = 'none';
}
}
checkEndpoint();.
But still, I thank you very much

How to hide a row of a table which does not contains a specific icon

I have a table with some items and those items can be selected by adding a tick. Check the image attached:
What I need to achieve is to hide the row which does not contain any ticks to be not visible. This is because in my app I have to generate lists of the items contains only ticks in another view. So when I will press the generate button that row will be hidden.
I just want to say if that row does not contain any 'glyphicon-ok' need to be deleted/hidden when I will generate the view with the list of those items.
I tried something like this:
SveCrf.prototype.hideRowWhereNoTicksForm = function () {
var tr = document.getElementsByTagName('tr');
for (var i = 0; i < tr.length; i++) {
switch (tr.item(i).getElementsByTagName('td').item(0).className) {
case "glyphicon-ok":
tr.item(i).style.display = "none";
break;
}
}
}
This doesn't do anything.
I would like to see an example of being able to resolve this issue.
Correct me if I'm wrong but you don't seem to have provided HTML you want to act upon but just a screenshot and a link to some RoR code in the comments that generates the HTML. Also you don't show how you try to execute SveCrf.prototype.hideRowWhereNoTicksForm, and furthermore I'm not really sure at all what you are trying to do with switch/case (I also don't understand what item is supposed to be; this is where providing us with actual HTML might have helped).
In addition, as I've alluded to in some comments of mine, you are really trying to do two things. I don't know if you've seen this Stackoverflow page yet about creating "a Minimal, Complete, and Verifiable example" but I think reviewing that will help improve your StackOverflow experience moving forward (and also for me it validated my suggestion of "divide and conquer").
All of which I think made it hard for you to get the help you desired. In any case below I'm providing some sample HTML with a table containing four rows total, two with a cell that contains the class foo, and two that don't. Beneath that is my non-jQuery code selecting the rows with no cells containing the class foo, and then hiding them; furthermore there is a demo of the same functionality using jQuery at https://repl.it/#dexygen/HideRowsWithNoCellsWithClass
<table border="1">
<tr><td class='foo'>foo</td><td></td><td></td></tr>
<tr><td></td><td>bar</td><td></td></tr>
<tr><td></td><td></td><td>baz</td></tr>
<tr><td class="foo">foo</td><td>bar</td><td>baz</td></tr>
</table>
/*
We cannot call `filter` directly on an HTMLCollection such as returned by
"document.getElementsByTagName('tr')" as it is not a bona fide array, so we use
"[].filter.call()", and we return only those rows that *fail* the test
"row.querySelector('td.foo')", then we loop over these with `forEach` and hide them
*/
[].filter.call(document.getElementsByTagName('tr'), function(row) {
return !row.querySelector('td.foo');
}).forEach(function(row) {row.style.display = 'none'});

Select text in a column of an html table

Is it possible to select the text (i.e. have it highlighted so that it can be copy+pasted) of every cell in one vertical column of an HTML table.
Is there a JavaScript method, or perhaps an equivalent in some browsers to the Alt-Click-Drag shortcut used in many text editors?
Or is this impossible?
What you're looking for is called Range object (TextRange in IE).
Update:
Here's a working code to do what you're suggesting: http://jsfiddle.net/4BwGG/3/
While capturing cell contents, you can format them in any manner you wish. I'm simply appending a new line every time.
Note:
Works fine in FF 3 and above
IE (before 9) and Chrome do not support multiple selection.
Chrome doesn't highlight all cells (but captures all content). Same goes for IE9
IE 7 & 8 will throw an error.
An alternative is apply a CSS style that simulates highlighting on click of column header and loop through all cells to capture their content. Look and feel of this approach may differ from native selection's look (unless you somehow capture select event and alter the appearnce).
Then use jQuery copy plugin to copy them to clipboard.
Some code review tools implement this to allow copying & pasting code from one side of a side-by-side diff. I looked into how ReviewBoard pulls it off.
The gist is:
When a column selection begins, style the cells in all other columns with user-select: none (and its prefixed variants, if necessary). This creates the appearance of a column selection. The other columns are still secretly selected, so you have to...
Intercept the copy event and change its payload to reflect the contents of the selected column.
The ReviewBoard code to do this consists of this CSS and this JavaScript.
I pulled it out into a fairly minimal jsbin demo.
Here's the CSS to create the appearance of a single-column selection (you add the selecting-left class to the table when the left column is being selected, or selecting-right for the right):
.selecting-left td.right,
.selecting-left td.right *,
.selecting-right td.left,
.selecting-right td.left *,
user-select: none;
}
.selecting-left td.right::selection,
.selecting-left td.right *::selection,
.selecting-right td.left::selection,
.selecting-right td.left *::selection,
background: transparent;
}
Here's the JavaScript to intercept the copy event and plug in a single column's worth of data:
tableEl.addEventListener('copy', function(e) {
var clipboardData = e.clipboardData;
var text = getSelectedText();
clipboardData.setData('text', text);
e.preventDefault();
});
function getSelectedText() {
var sel = window.getSelection(),
range = sel.getRangeAt(0),
doc = range.cloneContents(),
nodes = doc.querySelectorAll('tr'),
text = '';
var idx = selectedColumnIdx; // 0 for left, 1 for right
if (nodes.length === 0) {
text = doc.textContent;
} else {
[].forEach.call(nodes, function(tr, i) {
var td = tr.cells[tr.cells.length == 1 ? 0 : idx];
text += (i ? '\n' : '') + td.textContent;
});
}
return text;
}
There's also some less interesting code to add the selecting-left and selecting-right classes at the start of a selection. This would require a bit more work to generalize to n-column tables.
This seems to work well in practice, but it's surprising how hard it is!
Here is a hack that doesn't involve javascript at all:
Step 1: open the inspector
For Chrome on mac, press command + option + J.
Step 2: select a random cell using the selector tool
For Chrome on mac, click the selector icon on the top left corner of the inspector to enter the selector mode.
Then click a random cell in the table.
Step 3: hide all cells by editing CSS
Click the New Style Rule button (see image below)
then enter this rule (you may want to modify it a little bit depending on your HTML)
tr td {
display: none; # hide all cells
}
Now all cells should have disappeared.
Step 4: display only the column that you want by editing CSS
Go ahead and add another rule above that one:
tr td:nth-child(2) { # replace 2 with the index of the column you want to copy. 2 means the second column
display: table-cell; # display that column
}
Now the column you want to copy from should have reappeared.
All the other columns should be invisible and can't be selected.
Step 5: just copy that column!
Note
You can restore the page by refreshing.
I find this work perfectly if you just want to select one column or two.
You could have a div which gets populated with the column data on click and apply a css class to give the columns the appearence of being selected
something like this:
var $mytable = $("#mytable"),
$copydiv = $("#copy_div");
$mytable.find("td").click(function(){
//get the column index
var $this = $(this),
index = $this.parent().children().index($this);
//find all cells in the same column
$mytable.find("tr:nth-child(" + index + ")").removeClass("selected").each(function () {
var $this = $(this);
$this.addClass("selected");
$copydiv.html($this.html() + "<br />");
});
});
or you could have a separate table for each column, but I don't think that would be worth it.
WIP: CSS only solution using :has() selector
The new :has() selector gave me hope in solving this issue without JS. The idea was to disable text selection for all cells, and only activate it for cells of a column that is hovered.
So you would have rule like this:
table:has(tr td:nth-child(1):hover) tr td:nth-child(1) {
-webkit-user-select: auto;
user-select: auto;
}
A complete sample can be found here: https://codepen.io/catlan/pen/XWELegW
This is work in progress, because in the current version of Safari (15.6.1), the display of the text range disappears after the selection is done, only to reappear after moving the cursor for a few pixel. See https://bugs.webkit.org/show_bug.cgi?id=244445
It seems to work fine in Chrome starting with Version 105.

Is there any way of having TinyMCE options selected by default?

Say I want to have the 'bold' option selected by default when the editor is initialized. How would I do that?
EDIT: A solution working with what Thariama answered is the following. I discovered that having the <p> tag left in there screwed with selecting other options, like with different headlines, and as I have no need for a <p> tag in this particular editor I did this in order to get <h1> by default:
setup: function(ed) {
ed.onInit.add(function(ed) {
if ($('p', ed.getDoc()).length > 0) {
$('p', ed.getDoc()).remove();
$('<h1><br mce_bogus="1" /></h1>').appendTo($('body', ed.getDoc()));
}
});
}
This works great even when the user jumps between selection options in a select.
EDIT2: This seems to work if you do need the <p> tag. This makes it bold by default:
setup: function(ed) {
ed.onInit.add(function(ed) {
if ($('p', ed.getDoc()).children().length == 1 && $('p', ed.getDoc()).children(':first').is('br')) {
$('p', ed.getDoc()).html('<b><br mce_bogus="1" /></b>');
}
});
}
Rather than doing it when the user enters it, why not output the values inside a bold tag when you output the data to the user outside of tinyMCE? That way, the user has no ability to override the option as they would if you simply had a tag inside.
As an aside, if you're using TinyMCE for a wysiwg web editor in an environment where users first generate code in Microsoft Word, there are significant issues with getting "junk" word code in via paste. Tiny's solution is a "paste from word" button, which users seem to often ignore -- I've deployed Tiny in 50+ business websites, and it's been a major issue with a majority of the clients using those sites. I switched over to CKEditor, which does word code-stripping on the FRONT end, and all's been well. People have their preferences, and I even liked using Tiny more. But Word Code issues have become a dealbreaker for me and I won't install Tiny any more.
The procedure of choice depends on many factors. If you have an empty tinymce editor you can initialize your editor with the following content
<p><strong><strong></p>
Please update your question regarding your use case.
EDIT: This might work better for you. Add this handler to one of your own plugins.
It checks for all paragraphs (if your tinymce uses divs you need to alter this piece of code)
and wraps the inner HTML into b-tags (you might want to use strong-tags).
ed.onInit.add(function(ed){
ps = ed.getDoc().getElementsByTagName('p');
for (var i=0; i < ps.length ; i++) {
ps[i].innerHTML = '<b>'+ps[i].innerHTML+'</b>';
}
});
Another option you have is to not use an own plugin, but to use the setup parameter when initializing tinymce:
setup : function(ed) {
ed.onInit.add(function(ed){
ps = ed.getDoc().getElementsByTagName('p');
for (var i=0; i < ps.length ; i++) {
ps[i].innerHTML = '<b>'+ps[i].innerHTML+'</b>';
}
});
},

Categories