There are some similar words in a string and I need to highlight only one word out of it. I have index value of this specific word. I'm using Mark JS for this. I need to do this in filter part of mark JS function. But not sure how will I get index value of each word while filtering.
Here is the fiddle to test
Code JS
$(function() {
$content = $(".content"),
currentIndex = 0;
var indexVal = 40;
$('#markBtn').on("click", function() {
var searchVal = " ipsum ";
$content.unmark({
done: function() {
console.log("unmark")
$content.mark(searchVal, {
"element": "span",
"className": "mark",
"accuracy": "partially",
"iframes": true,
"ignoreJoiners": true,
"acrossElements": true,
"separateWordSearch": true,
"diacritics": false,
"filter": function (textNode, foundTerm, totalCounter, counter) {
console.log("marks - filter " + textNode+" "+foundTerm);
//check indexVal with foundTerm and return true
return true;
},
"each": function (node) {
console.log("marks - each " + node);
},
"done": function() {
console.log("marks - done ");
}
});
}
});
});
});
HTML
<div class="header">
Mark second word 'ipsum' in the text below.
<button id="markBtn">Mark Text</button>
</div>
<div class="content">
<p>Lorem ipsum dolor sit amet, consectetur ipsum adipiscing elit ipsum.</p>
</div>
Refer this Link.
it may help u
http://jsfiddle.net/sadhique92/HfS7e/1231/
<script>
function highlightSearch() {
var text = document.getElementById("query").value;
var query = new RegExp("(\\b" + text + "\\b)", "gim");
var e = document.getElementById("searchtext").innerHTML;
var enew = e.replace(/(<span>|<\/span>)/igm, "");
document.getElementById("searchtext").innerHTML = enew;
var newe = enew.replace(query, "<span>$1</span>");
document.getElementById("searchtext").innerHTML = newe;
}
</script>
<style>
#searchtext span{
background-color:#FF9;
color:#555;
}
div {
padding: 10px;
}
</style>
<div><h2>Find and highlight text in document</h2>
<form action="" method="" id="search" name="search">
<input name="query" id="query" type="text" size="30" maxlength="30">
<input name="searchit" type="button" value="Search" onClick="highlightSearch()">
</form></div>
<div id="searchtext">
<p>JavaScript is the programming language of the Web. The overwhelming majority of
modern websites use JavaScript, and all modern web browsers—on desktops, game
consoles, tablets, and smart phones—include JavaScript interpreters, making Java-
Script the most ubiquitous programming language in history. JavaScript is part of the
triad of technologies that all Web developers must learn: HTML to specify the content
of web pages, CSS to specify the presentation of web pages, and JavaScript to specify
the behavior of web pages. This book will help you master the language.</p>
<p>If you are already familiar with other programming languages, it may help you to know
that JavaScript is a high-level, dynamic, untyped interpreted programming language
that is well-suited to object-oriented and functional programming styles. JavaScript
derives its syntax from Java, its first-class functions from Scheme, and its prototypebased
inheritance from Self. But you do not need to know any of those languages, or
be familiar with those terms, to use this book and learn JavaScript.</p>
<p>The name "JavaScript" is actually somewhat misleading. <span>Except</span> for a superficial syntactic
resemblance, JavaScript is completely different from the Java programming language.
And JavaScript has long since outgrown its scripting-language roots to become
a robust and efficient general-purpose language. The latest version of the language (see
the sidebar) defines new features for serious large-scale software development.</p>
</div>
The filter should return the current index that you want to mark:
"filter": function (textNode, foundTerm, totalCounter, counter) {
console.log("marks - filter " + textNode+" "+foundTerm);
var result = totalCounter == currentIndex;
return result;
},
so if you put currentIndex = 0 it will select the first matching and so on ...
Your question: not sure how will I get index value of each word while filtering?
The Mark JS documentation read as follows:
filter function: A callback to filter or limit matches. It will be called for each match and receives the following parameters:The text node which includes the matchThe term that has been foundA counter indicating the total number of all marks at the time of the function callA counter indicating the number of marks for the termThe function must return false if the mark should be stopped, otherwise true.
In order words: the for forth parameter is the counter(index) of each word found.
Related
I'm building a search auto-suggest component where the results are rendered with hyperHTML.
The matching string part of the suggestions returned from the server should be highlighted.
I'm using a RegEx and String.prototype.replace to highlight the matching part, but somehow I can't manage to output the return value of it to HTML. It just renders the <strong> tags as strings.
I tried quite a bit of different approaches to solve this but without any success and am running out of ideas...
This is my rendering function:
const suggestionsContainer = document.querySelector(
".js-suggestions-container"
);
const suggestions = [{
title: "lorem ipsum dolor sit amet",
url: "#"
},
{
title: "lorem ipsum dolor sit amet",
url: "#"
}
];
let query = "ipsum";
function renderSuggestions(suggestions, query) {
const queryRegEx = new RegExp(query, "gi");
hyperHTML.bind(suggestionsContainer)`
${suggestions.map((suggestion) => hyperHTML.wire(suggestion)`
<a href="${suggestion.url}">
${hyperHTML.wire()`${suggestion.title.replace(queryRegEx, "<strong>$&</strong>")}`}
</a>
`)}
`;
}
renderSuggestions(suggestions, query);
a {
display: block;
margin: 1rem 0;
}
<script src="https://unpkg.com/hyperhtml#latest/min.js"></script>
<div class="js-suggestions-container"></div>
As you can see in this CodePen, the only change you need is to explicitly ask for html:
${suggestions.map((suggestion) => hyperHTML.wire(suggestion)`
<a href="${suggestion.url}">
${{html: suggestion.title.replace(queryRegEx, "<strong>$&</strong>")}}
</a>
`)}
The {html: ...} is the most obvious way, but hyperHTML also injects arrays as HTML, but that might be unexpected, while both lighterhtml and micro html are safer by default, and interpolated content must be always explicitly injected.
P.S. wiring just text as content is also almost never necessary
I have problem with adding HTML elements in my template string. I want to add new lines in my <li> element, but <br> is interpreted like string.
let movieDescription = document.createTextNode(`${moviesData[i].title} <br> ${moviesData[i].year} <br> ${moviesData[i].genre} <br>${moviesData[i].summary}`);
How can I add <br> element in template string?
As you have already been informed, <br> is HTML not text. So you'll need to parse the Template Literal in order to render line breaks correctly. The most common way to do it is by using the property .innerHTML, although I've read plenty of posts and blogs about how crappy it is, I've never had a problem with it. In this example, we are using insertAdjacentHTML() (note the template literal has <div>s and <hr>s):
var movieDescription = `
<hr>
<div>Title: ${moviesData[i].title}</div>
<div>Year: ${moviesData[i].year}</div>
<div>Genre: ${moviesData[i].genre}</div>
<div>Summary: ${moviesData[i].summary}</div>
<hr>`;
document.querySelector('.dock').innerHTML = movieDescription;
An alternative method is insertAdjacentHTML(). It's like innerHTML on steroids.
Pros:
it's faster and safer than innerHTML
it allows us to specifically determine where the insertion should be relating to the target element:
beforebegin: inserted HTML <div>target element</div>
afterbegin: <div> inserted HTML target element</div>
beforeend: <div>target element inserted HTML </div>
afterend: <div>target element</div> inserted HTML
It doesn't overwrite content like innerHTML does.
Cons:
It's verbose.
Demo
var dock = document.querySelector('.dock');
var i;
var moviesData = [{
title: 'Pulp Fiction',
year: '1994',
genre: 'Drama-Crime',
summary: "You will know , my name is the Lord, when I lay my vengance upon thee!"
}, {
title: 'Reservoir Dogs',
year: '1992',
genre: 'Drama-Crime',
summary: "Clowns to the left of me, jokers to the right! Here I am stuck in the middle with you"
}];
for (i = 0; i < moviesData.length; i++) {
var movieDescription = `
<hr>
<div>Title: ${moviesData[i].title}</div>
<div>Year: ${moviesData[i].year}</div>
<div>Genre: ${moviesData[i].genre}</div>
<div>Summary: ${moviesData[i].summary}</div>
<hr>`;
dock.insertAdjacentHTML('beforeend', movieDescription);
}
<div class='dock'></div>
A text node contains... well... text. It does not contain other html elements, and thus when you create one it will interpret the <br> as text and display it as is.
What you want would be a collection of TextNodes and HTMLBRElement.
const movieDescription = [
document.createTextNode(moviesData[i].title),
document.createElement('br'),
document.createTextNode(moviesData[i].year),
document.createElement('br'),
document.createTextNode(moviesData[i].genre),
document.createElement('br'),
document.createTextNode(moviesData[i].summary)
];
That is quite awkward to do. Instead, you probably want to use Element.innerHTML on the element you want to add this description to.
const html = `${moviesData[i].title} <br> ${moviesData[i].year} <br> ${moviesData[i].genre} <br>${moviesData[i].summary}`;
document.querySelector('.my-movie-description').innerHTML = html;
A similar method exists to add a single TextNode to an element. This is Element.innerText.
I have witten a html templating engine to render dynamic data at browser.
Codepen example
var moviesData = [];
for(var i = 0; i < 10; i++){
moviesData.push( {
"title" : "title " + i,
"year" : 2000 + i,
"genre" : "action",
"summary" : "summary " + i
});
}
body {
background-color: #a3d5d3;
}
.movie {
margin: 0.5em 1em ;
padding : 0.2em 0.5em;
border: 1px solid;
}
<div jstl-autorun jstl-foreach="${moviesData}" jstl-foreach-var="movieData">
<div class="movie">
${movieData.title}
<br/>
${movieData.year}
<br/>
${movieData.genre}
<br/>
${movieData.summary}
</div>
</div>
here is the link for the template engine
I hope it's useful for you.
So I know nothing about angularJS and I haven't found any good explanation on how to bind a dictionary with an html tag so that every change in the dictionary produces a change in the number inside the <p> tag. For example if I have multiple dictionaries inside an array (one for each player)
var players = [{bambu:0, clouds:0, fruits:0}, {bambu:0, clouds:0, fruits:0} , {bambu:0, clouds:0, fruits:0}]
I want to display the number associated with bambu in a <p> tag
<p id="player_0_bambu" class="number_of_cards"> </p>
I want to change it dinamically like angular does.
I tried to do a
while (true){
for (var r=0; r< num_players;r++){
for (var c=0; c< colores.length;c++){
$("#player_"+r+"_"+colores[c]).html(players[r][colores[c]])
}
}
}
but a while true just crashes javascript, so I turned to angular but I find it difficult to understand.
Thank you for your help!
Not sure why angular would be an option but judging by your description you just loop through the players array and use the values from the object at hand to query for <p/> tags.
var players = [{ bambu:0, clouds:0, fruits:0 }, {bambu:0, clouds:0, fruits:0 } , { bambu:0, clouds:0, fruits:0} ]
players.forEach(player => {
Object.keys(player).forEach(key => {
const val = player[key];
document.querySelector(`#player_${val}_${key}`).innerHTML = val;
});
});
I have a function called speak. It takes in an object filled with arrays of fragments of sentences and spits out a random phrase. I have two of these objects. Each with unique sentence fragments. Via radio button I would like to be able to choose which object is sent through the function and then push a button to make that happen. When I hard wire the function with one of the buttons it works fine, but that is not what I am going for. I have tried several different suggested methods from this site and others with no luck. The closest I get is when I can get the name of the object into the speak function, but it is only recognized as a string. Here is my html...
<html>
<head>
</head>
<body>
<!-- <script type="text/javascript" src="wrestlingGame.js"></script> -->
<div>
<input type="radio" name="speak" value=ToolBelt.commentary onClick="ToolBelt.handleClick(this)">commentary a<br/>
<input type="radio" name="speak" value=ToolBelt.commentary1 onClick="ToolBelt.handleClick(this)">commentary b<br/>
</div>
<button onclick="ToolBelt.speak()">Commentary</button>
<div id="commentator"></div>
<div id="lif"></div>
</body>
this version is not wired to the 'Commentary' button. It is instead wired to the radio buttons themselves and it does not work correctly. I am posting this because it is my most recent attempt.
Here is my complete javascript including the two objects and the speak function...
var ToolBelt = {
commentary:{
exclamations: ["Wow! ", "Oh no! ", "God almighty! ", "Good Gracious! "],
leadIn: ["That was a nasty ", "What a powerful ", "A horrifying ", "That was an illegal "],
strikes: ["uppercut!", "chairshot!", "Lucy lick!", "monkey punch!", "jab", "Bug meow!", "dropkick!"],
},
commentary1:{
exclamations: ["Moo! ", "Quack! ", "Bark! ", "Growl! "],
leadIn: ["Chupa chup ", "Spaghetti ", "Bubbling ", "Necktie "],
strikes: ["uppercut!", "chairshot!", "Lucy lick!", "monkey punch!", "jab", "Bug meow!", "dropkick!"],
},
handleClick:function(object){
this.speak(object.value);
},
speak:function(i){
var string='';
for(key in i){
var arr = i[key];
var x = this.random(0,arr.length);
string += arr[x] + " ";
}
document.getElementById("commentator").innerHTML = string;
},
random: function(max, min){
return Math.floor(Math.random()*(max-min)+min);
}
};
There are a few problems here.
First, you cannot assign a JavaScript object as the value of an input element. Input element values can be strings only. What we can do with the value is have it contain the key of the ToolBelt object that we want to target:
<input type="radio" name="speak" value="commentary" onClick="ToolBelt.handleClick(this)">commentary a
<br/>
<input type="radio" name="speak" value="commentary1" onClick="ToolBelt.handleClick(this)">commentary b
Next, you must understand what the this is in the onClick handlers on your input elements. In this context, this refers to the DOM element itself. So in the ToolBelt.handleClick method, we will want to get the value from the passed DOM element:
handleClick: function (el) {
this.speak(this[el.value]);
}
This will work, but it will generate commentary whenever we check one of the radio buttons. However, the presence of the "Commentary" button suggests that we want to generate commentary only when this button is clicked. To achieve this, we will have to remove the call to speak in our handleClick method and instead cache the currently selected commentary key:
currentCommentaryKey: null,
handleClick: function (el) {
this.currentCommentaryKey = el.value;
}
Then we will alter the speak method to make use of the cached key (I have added a check to make sure the current commentary key is valid):
speak: function () {
if (!this.hasOwnProperty(this.currentCommentaryKey)) { return; }
var i = this[this.currentCommentaryKey];
/* rest of speak method implementation */
}
This should be easy for a Javascript expert.
For those who don't know what schema is ( http://schema.org ), it's a new way for Search Engines to read content on a webpage. It works by tagging relevant data with specific tags.
For those who do know what it is, here is a chrome extension (Schema Explorer) that makes it easy to inspect what your data looks like on your page. See the example.
NOW: There is a tiny issue with the extension where by is does not skip/ignore empty nested elements. Here are two examples: The first works perfectly but, the second bombs because of the empty <div> tag:
First example works:
<div itemscope="" itemtype="http://schema.org/Movie">
<h1 itemprop="name">Avatar</h1>
<div itemprop="director" itemscope="" itemtype="http://schema.org/Person">
Director: <span itemprop="name">James Cameron</span> (born <span itemprop="birthDate">August 16, 1954</span>)
</div>
<span itemprop="genre">Science fiction</span>
Trailer
</div>
Seconds example gives issues:
<div itemscope="" itemtype="http://schema.org/Movie">
<div>
<h1 itemprop="name">Avatar</h1>
<div itemprop="director" itemscope="" itemtype="http://schema.org/Person">
Director: <span itemprop="name">James Cameron</span> (born <span itemprop="birthDate">August 16, 1954</span>)
</div>
<span itemprop="genre">Science fiction</span>
Trailer
</div>
</div>
I had a look at the extension and it's actually very well put together with one javascript file doing most of the work. Here is the code that does the looping, however it needs to be able to skip empty nested elements and perhaps be a little bit more robust in general:
var __explore = function(node, parentData)
{
if (parentData === null || parentData === undefined)
{
parentData = __dataTree;
}
if (node.getAttribute)
{
var isItemScope = node.getAttribute('itemscope');
var hasItemProp = node.getAttribute('itemprop');
var itemtype = node.getAttribute('itemtype');
var childs = node.childNodes;
var i = 0;
var tmp = new Array();
while (i < childs.length)
{
if (isItemScope !== null)
__explore(childs[i], tmp);
else
__explore(childs[i], null);
++i;
}
if (isItemScope !== null)
{
parentData.push({name : 'scope', value : hasItemProp, type : itemtype, childs : [tmp], node : node});
}
else if (hasItemProp && parentData)
{
parentData.push({name : hasItemProp, value : node.innerText});
}
}
}
Here is the complete versions of the contentscript.js https://gist.github.com/3413475
Hopefully someone can help me with this. For the record I've contacted the author but he's been preoccupied with more urgent matters.
I made it work as expected: http://jsfiddle.net/vyrvp/1/ but I must confess that this is a bit hackish. This code may need some more refactoring to make it work for all cases and make it readable.