Having the reference to a specific DOM element (e.g. <mark>), how can we get the full word containing that element?
For example :
H<mark>ell</mark>o Wor<mark>l</mark>d, and He<mark>llo</mark>, <mark>Pluto</mark>!
I expect to get the following output :
First <mark>: Hello
Second: World
Third: Hello
Fourth: Pluto
var $marks = $("mark");
var tests = [
"Hello",
"World",
"Hello",
"Pluto",
];
function getFullWord($elm) {
// TODO: How can I do this?
// This is obviously wrong.
return $elm.html();
}
var $marks = $("mark");
tests.forEach(function(c, i) {
var word = getFullWord($marks.eq(i));
if (word !== c) {
alert("Wrong result for index " + i + ". Expected: '" + c + "' but got '" + word + "'");
}
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
H<mark>ell</mark>o Wor<mark>l</mark>d, and He<mark>llo</mark>, <mark>Pluto</mark>!
If you need fast and compact code (one-liner), try this:
var $marks = $('mark');
$marks.each(function() {
var wholeWord = (this.previousSibling.nodeValue.split(' ').pop() +
this.textContent +
this.nextSibling.nodeValue.split(' ')[0]
).replace(/[^\w\s]/gi, '');
});
JSFiddle (with logging into console and comments)
Related
The code is used in a HTML document, where when you press a button the first word in every sentence gets marked in bold
This is my code:
var i = 0;
while(i < restOftext.length) {
if (text[i] === ".") {
var space = text.indexOf(" ", i + 2);
var tekststykke = text.slice(i + 2, space);
var text = text.slice(0, i) + "<b>" + tekststykke + "</b>" + text.slice(i + (tekststykke.length + 2));
var period = text.replace(/<b>/g, ". <b>");
var text2 = "<b>" + firstWord + "</b>" + period.slice(space1);
i++
}
}
document.getElementById("firstWordBold").innerHTML = text2;
}
It's in the first part of the code under function firstWordBold(); where it says there is an error with
var space1 = text.indexOf(" ");
Looks like you're missing a closing quote on your string, at least in the example you provided in the question.
Your problem is the scope of the text variable. In firstWordBold change every text to this.text, except the last two where you re-define text
Also, if you want to apply bold to the first word this is easier...
document.getElementById('test-div-2').innerHTML = '<b>' + firstWord + '</b>' + restOftext;
It now works for me, with no errors and it applies bold to the first word.
Here's how the function ended up,
function firstWordBold() {
console.log('bolding!');
var space1 = this.text.indexOf(' ');
var firstWord = this.text.slice(0, space1);
var restOftext = this.text.slice(space1);
document.getElementById('test-div-2').innerHTML = '<b>' + firstWord + '</b>' + restOftext;
}
To make every first word bold, try this...
function firstWordBold() {
let newHTML = '';
const sentences = this.text.split('.');
for (let sentence of sentences) {
sentence = sentence.trim();
var space1 = sentence.indexOf(' ');
var firstWord = sentence.slice(0, space1);
var restOftext = sentence.slice(space1);
newHTML += '<b>' + firstWord + '</b>' + restOftext + ' ';
}
document.getElementById('test-div-2').innerHTML = newHTML;
}
One last edit, I didn't notice you had sentences ending with anything other that a period before. To split on multiple delimiters use a regex, like so,
const sentences = this.text.split(/(?<=[.?!])\s/);
Okay, that title will sound a bit crazy. I have an object, which I build from a bunch of inputs (from the user). I set them according to their value received, but sometimes they are not set at all, which makes them null. What I really want to do, it make an item generator for WoW. The items can have multiple attributes, which all look the same to the user. Here is my example:
+3 Agility
+5 Stamina
+10 Dodge
In theory, that should just grab my object's property name and key value, then output it in the same fashion. However, how do I setup that if-statement?
Here is what my current if-statement MADNESS looks like:
if(property == "agility") {
text = "+" + text + " Agility";
}
if(property == "stamina") {
text = "+" + text + " Stamina";
}
if(property == "dodge") {
text = "+" + text + " Dodge";
}
You get that point right? In WoW there are A TON of attributes, so it would suck that I would have to create an if-statement for each, because there are simply too many. It's basically repeating itself, but still using the property name all the way. Here is what my JSFiddle looks like: http://jsfiddle.net/pm2328hx/ so you can play with it yourself. Thanks!
EDIT: Oh by the way, what I want to do is something like this:
if(property == "agility" || property == "stamina" || ....) {
text = "+" + text + " " + THE_ABOVE_VARIABLE_WHICH_IS_TRUE;
}
Which is hacky as well. I definitely don't want that.
if(['agility','stamina','dodge'].indexOf(property) !== -1){
text = "+" + text + " " + property;
}
If you need the first letter capitalized :
if(['agility','stamina','dodge'].indexOf(property) !== -1){
text = "+" + text + " " + property.charAt(0).toUpperCase() + property.substr(1);
}
UPDATE per comment:
If you already have an array of all the attributes somewhere, use that instead
var myatts = [
'agility',
'stamina',
'dodge'
];
if(myatts.indexOf(property) !== -1){
text = "+" + text + " " + property.charAt(0).toUpperCase() + property.substr(1);
}
UPDATE per next comment:
If you already have an object with the attributes as keys, you can use Object.keys(), but be sure to also employ hasOwnProperty
var item = {};
item.attribute = {
agility:100,
stamina:200,
dodge:300
};
var property = "agility";
var text = "";
if(Object.keys(item.attribute).indexOf(property) !== -1){
if(item.attribute.hasOwnProperty(property)){
text = "+" + text + " " + property.charAt(0).toUpperCase() + property.substr(1);
}
}
Fiddle: http://jsfiddle.net/trex005/rk9j10bx/
UPDATE to answer intended question instead of asked question
How do I expand the following object into following string? Note: the attributes are dynamic.
Object:
var item = {};
item.attribute = {
agility:100,
stamina:200,
dodge:300
};
String:
+ 100 Agility + 200 Stamina + 300 Dodge
Answer:
var text = "";
for(var property in item.attribute){
if(item.attribute.hasOwnProperty(property)){
if(text.length > 0) text += " ";
text += "+ " + item.attribute[property] + " " + property.charAt(0).toUpperCase() + property.substr(1);
}
}
It's unclear how you're getting these values an storing them internally - but assuming you store them in a hash table:
properties = { stamina: 10,
agility: 45,
...
}
Then you could display it something like this:
var text = '';
for (var key in properties) {
// use hasOwnProperty to filter out keys from the Object.prototype
if (h.hasOwnProperty(k)) {
text = text + ' ' h[k] + ' ' + k + '<br/>';
}
}
After chat, code came out as follows:
var item = {};
item.name = "Thunderfury";
item.rarity = "legendary";
item.itemLevel = 80;
item.equip = "Binds when picked up";
item.unique = "Unique";
item.itemType = "Sword";
item.speed = 1.90;
item.slot = "One-handed";
item.damage = "36 - 68";
item.dps = 27.59;
item.attributes = {
agility:100,
stamina:200,
dodge:300
};
item.durability = 130;
item.chanceOnHit = "Blasts your enemy with lightning, dealing 209 Nature damage and then jumping to additional nearby enemies. Each jump reduces that victim's Nature resistance by 17. Affects 5 targets. Your primary target is also consumed by a cyclone, slowing its attack speed by 20% for 12 sec.";
item.levelRequirement = 60;
function build() {
box = $('<div id="box">'); //builds in memory
for (var key in item) {
if (item.hasOwnProperty(key)) {
if (key === 'attributes') {
for (var k in item.attributes) {
if (item.attributes.hasOwnProperty(k)) {
box.append('<span class="' + k + '">+' + item.attributes[k] + ' ' + k + '</span>');
}
}
} else {
box.append('<span id="' + key + '" class="' + item[key] + '">' + item[key] + '</span>');
}
}
}
$("#box").replaceWith(box);
}
build();
http://jsfiddle.net/gp0qfwfr/5/
I have a very simple snipplet for a json array and a javascript function that now returns a single argument:
<!DOCTYPE html>
<html>
<body>
<h2>JSON Array Test</h2>
<p id="outputid"></p>
<script>
var arrayinput = '{"collection":[' +
'{"firstAttr":"XXXA","secAttr":"13156161","lastAttr":"01" },' +
'{"firstAttr":"XXXB","secAttr":"11153325","lastAttr":"02" },' +
'{"firstAttr":"XXXC","secAttr":"14431513","lastAttr":"03" },' +
'{"firstAttr":"XXXC","secAttr":"161714","lastAttr":"01" },' +
'{"firstAttr":"XXXC","secAttr":"151415","lastAttr":"02" },' +
'{"firstAttr":"XXXC","secAttr":"114516","lastAttr":"02" },' +
'{"firstAttr":"XXXC","secAttr":"131417","lastAttr":"03" },' +
'{"firstAttr":"XXXC","secAttr":"1311865","lastAttr":"03" },' +
'{"firstAttr":"XXXC","secAttr":"1314153","lastAttr":"01" },' +
'{"firstAttr":"XXXC","secAttr":"13312163","lastAttr":"01" }]}';
obj = JSON.parse(arrayinput);
document.getElementById("outputid").innerHTML =
obj.collection[1].firstAttr + " " + obj.collection[1].secAttr;
</script>
</body>
</html>
Now the problem is that I don't want to return just one value but multiple ones. For example all entrys with lastAttr=01 should be returned.
Therefore I would need something along the line of:
for(var i in obj) {
if(lastAttr[i]="01") {
document.getElementById("outputid").innerHTML =
obj.collection[i].firstAttr + " " + obj.collection[i].secAttr;
} else {
}
}
Any idea on how to make this work?
If you want to perform a where you need to use Array.prototype.filter:
var filteredArr = arr.collection.filter(function(item) {
return item.lastAttr == "01";
});
And, finally, you can use Array.prototype.forEach to iterate results and perform some action:
var outputElement = document.getElementById("outputid");
filteredArr.forEach(function(item) {
// Check that I used insertAdyacentHtml to be sure that all items
// will be in the UI!
outputElement.insertAdjacentHTML("afterbegin", item.firstAttr + " " + item.secAttr);
});
Also, you can do it fluently:
var arr = {
collection: [{
firstAttr: "hello",
secAttr: "world",
lastAttr: "01"
}, {
firstAttr: "hello 2",
secAttr: "world 2",
lastAttr: "01"
}]
};
var outputElement = document.getElementById("outputid");
var filteredArr = arr.collection.filter(function(item) {
return item.lastAttr == "01";
}).forEach(function(item) {
outputElement.insertAdjacentHTML("afterbegin", item.firstAttr + " " + item.secAttr);
});
<div id="outputid"></div>
You need to iterate over the collection Array and append the new stuff. Right now you're iterating the outer object and overwriting the .innerHTML each time.
var out = document.getElementById("outputid");
for (var i = 0; i < obj.collection.length; i++) {
if(obj.collection[i].lastAttr=="01") {
out.insertAdjacentHTML("beforeend", obj.collection[i].firstAttr + " " + obj.collection[i].secAttr);
}
}
Note that I used == instead of = for the comparison, and .insertAdjacentHTML instead of .innerHTML.
if you want to replace html try this
(someCollection) array;
var r = new Array();
var j = -1;
r[++j] = '<ul class="list-group">';
for (var i in array) {
var d = array[i];
if (d.attribute== somevalue) {
r[++j] = '<li class="list-group-item">'
r[++j]=d.otherattribute;
r[++j] = '</li>';
}
}
r[++j] = '</ul>';
//for(var b in r) //alert to see the entire html code
//{ alert(r[b]);}
firstLoadOnPage = false;
var list = document.getElementById('SymptomSection');
list.innerHTML = r.join('');
this replaces the inside of element with classname "SymptomSection"
I'm having a small problem with a regexp pattern. I don't have regexp knowledge, so I couldn't solve it.
I have this text:
var text = "this (is) some (ran)dom text";
and I want to capture anything between (). So after following this tutorial I came up with this pattern:
var re = /(\(\w*\))/g;
which works fine. But what I want to do now is replace the found matches, or rather modify. I want to wrap the found matches with a span tag. So I used this code:
var spanOpen = '<span style="color: silver;">';
var spanClose = '</span>';
text.replace(re, spanOpen + text.match(re) + spanClose);
even though the code works, I don't get the result I want. It outputs:
as HTML
this <span style="color: silver;">(is),(ran)</span> some <span style="color: silver;">(is),(ran)</span>dom text
as text
this (is),(ran) some (is),(ran)dom text
You can check the example in fiddle. How can I fix this?
The code in fiddle:
var text = "this (is) some (ran)dom text";
var re = /(\(\w*\))/g;
var spanOpen = '<span style="color: silver;">';
var spanClose = '</span>';
var original = "original: " + text + "<br>";
var desired = "desired: this " +spanOpen+"(is)"+spanClose+ " some " +spanOpen+"(ran)"+spanClose+ "dom text<br>";
var output = "output: " + text.replace(re, spanOpen + text.match(re) + spanClose);
var result = original + desired + output;
document.body.innerHTML = result;
If the title is wrong or misleading, I'll change it.
The .replace() method can take a function as the 2nd parameter. That will come in handy here.
var output = "output: " + text.replace(re, function(match){
return spanOpen + match + spanClose
});
The function will be called for each individual match.
You can also use '$&' in your replace string to reference each match
var output = "output: " + text.replace(re, spanOpen + '$&' + spanClose);
See: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/replace
text.match(re) is returning an array of the result, so what you can do is loop this array and replace your string with each items, like this:
var matches = text.match(re);
var output = "output: " + text;
for (var i = 0; i < matches.length; i++)
{
output = output.replace(matches[i], spanOpen + matches[i] + spanClose);
}
See this FIDDLE
I am using prototype in my application but I am not sure how to add this correctly. Basically I have the following function and I need to construct the href of an anchor from which I already have the reference to a series of appended values
MyJavascriptClass.prototype.init = function() {
this.ToDate = $(this.Prefix + 'ToDate');
this.FromDate = $(this.Prefix + 'FromDate');
}
so in the following function I need to add those as parameters in the url attribute
MyJavascriptClass.prototype.btnClicked = function(evt) {
this.lnkShowLink.setAttribute('href', 'MyWebpage.aspx?StartDate=7/18/2012&EndDate=1/19/2012');
}
How can i do something like 'MyWebPage.aspx?StartDate=this.ToDate&EndDate=this.FromDate' ? Any help would be appreciated.
If you are using jquery, and $(this.Prefix + 'ToDate') and $(this.Prefix + 'FromDate') represent fields that contain values, then you can do this:
MyJavascriptClass.prototype.btnClicked = function(evt) {
this.lnkShowLink.setAttribute('href', 'MyWebpage.aspx?StartDate=' + this.ToDate.val() + '&EndDate=' + this.FromDate.val() + '');
}
It is difficult to tell from your code what they represent, and why you have them wrapped in $(..).
If ToDate and FromDate contain the two date values, then this should work...
'MyWebPage.aspx?StartDate=' + this.ToDate + '&EndDate=' + this.FromDate
If you don't know every properties:
var properties = [];
for(var i in this)
if(this.hasOwnProperty(i))
properties.push(i+'='+this[i]);
var url = 'MyWebPage.aspx?'+properties.join('&');
var string = "My name is: ",
name = "Bob",
punctuation = ".",
greeting = string + name + punctuation;
Or
var User = { name : "Bob", age : 32, sign : "Leo" },
welcome = "Hi, I'm " + User.name + ", and I'm " + User.age + " years old, I'm a " + User.sign + ", and I enjoy long walks on the beach.";