So this is the functionality I need to clean up:
I need to create a function where viewers can click on any word in a sentence, and it will be highlighted. However, I need to make it so only one word is highlighted at a time. So for example, if you click the word 'you' and then you change your mind and click the word 'eagle', then the word 'you' will be deselected.
There's already existing code for it, but we're working on a very tight deadline and if we make edits and adjustments using this extremely long, extremely difficult-to-navigate code, then we will use up so many precious weeks producing a single 5-minute interactivity.
This is what it looks like:
And this is a snippet of the JS (so you can glimpse the problem):
(the HTML and JS codes are available upon request)
So instead of this long hell that we might need to put ourselves through, I was thinking something like passing each sentence through an array so each individual word would already be assigned to a name. So then we would be able to call each array via a for loop to print it out on the page, and use an arrayName[i] to call individual words for highlighting. And then maybe an if-else statement so only the selected word is highlighted.
I've been trying to push the actual HTML elements through the arrays, like, only get the <p> for every <div id="sentence1"> or something, but it doesn't seem to be possible... If it is, please tell me how it is done, or if it is not, I will still appreciate any other alternative for this.
I cannot for the life of me figure out all the hard-coding on my own, as I only know so much about JavaScript right now, but am very, very willing to learn! Any help with this would be deeply appreciated, as we're working on a very tight deadline.
Thank you so much in advance! Please, any help, or any suggestions would do!
EDIT
This is the code for our checkAns() function. It increments var correct when a correct answer is highlighted every time checkAns() runs. It is also responsible for marking specific numbers wrong or right.
function checkAns(){
document.getElementById('alertMsg').style.visibility = "hidden";
if(Ans1B == "selected"){
correct++
document.getElementById('marksymbol1').className = "smile";
}
else
{
document.getElementById('marksymbol1').className = "sad";
}
if(Ans2A == "selected"){
correct++
document.getElementById('marksymbol2').className = "smile";
}
else
{
document.getElementById('marksymbol2').className = "sad";
}
if(Ans3A == "selected"){
correct++
document.getElementById('marksymbol3').className = "smile";
}
else
{
document.getElementById('marksymbol3').className = "sad";
}
if(Ans4A == "selected"){
correct++
document.getElementById('marksymbol4').className = "smile";
}
else
{
document.getElementById('marksymbol4').className = "sad";
}
if(Ans5A == "selected"){
correct++
document.getElementById('marksymbol5').className = "smile";
}
else
{
document.getElementById('marksymbol5').className = "sad";
}
As per #DrewGoldsBerry's answer, with some example code. Here's a working fiddle of the code below: http://jsfiddle.net/E3D6T/1/
Set up your HTML with a class to indicate which lines should have the highlight functionality.
<p class="highlight">Each word will be wrapped in a span.</p>
<p class="highlight">A second paragraph here.</p>
In your JS, split the p elements into words wrapped in span tags, which can then be bound to a click function:
// wrap words in spans
$('p.highlight').each(function () {
var $this = $(this);
$this.html($this.text().replace(/\b(\w+)\b/g, "<span>$1</span>"));
});
// bind to each span
$('p.highlight span').click(function () {
$(this).parent().find('span').css('background-color', '');
$(this).css('background-color', '#ffff66');
});
edit:
http://jsfiddle.net/jorgthuijls/E3D6T/16/
I added all the answer checking to the click function itself. Should be fairly straight forward.
Sorry to add to the noise... my answer is very similar to Jorgs and Roberts, and it also checks for valid answers.
JS Fiddle is here:
http://jsfiddle.net/M7faZ/3/
The checkAns function uses the ID of the sentence element, to map the answer object to the selectedAnswer object.
The HTML has carefully chosen ID and classnames:
<ul class='sentences'>
<li class='sentence' id='ans1'>Can you draw an eagle?</li>
<li class='sentence' id='ans2'>She is good in painting.</li>
</ul>
<div id='mark-symbol-ans1'></div>
<div id='mark-symbol-ans2'></div>
And the JS has a map of answers.
// get the list
var $sentences = $('.sentence'),
answers = {
ans1: 'you',
ans2: 'She'
},
selectedAnswers = {};
function checkAns() {
var correct;
for (var i in answers) {
correct = selectedAnswers[i] === answers[i]
$('#mark-symbol-' + i).toggleClass('smile', correct);
$('#mark-symbol-' + i).toggleClass('sad', !correct);
}
}
If you care about people cheating, this part should be done on the server so it's not exposed to the client.
This could maybe work if the amount of content is limited else you might experience some lag onload.
Start out by gathering all the elements that need this ability into an array. Split each sentence using " " as the variable into another temporary array. Step through temp array adding a span tag around each word with a new class. Then return the new string with span tags to the corresponding element. This can be down with 2 for loops. Last bind both hover and onclick to each new tag by using another for loop. Sorry no code, I'm watching tosh lol, but if you make a jsfiddle I will write it for you.
Like I said this shouldn't be done with a lot of sentences at one time. You could always stagger sentences if you have multiple sections.
Hope this helps explain how I would do it. Good luck let me know what happens!
A similar solution to Jorg's, with some variations. I've done the wrapping with split() and join() instead of regex. I also tend to put my jQuery chains on separate lines for visual clarity.
<html>
<head>
<script type="text/javascript" src="./jquery.min.js"></script>
<script type="text/javascript">
$( document ).ready( function() {
var p = $( "p", "#myDiv" );
$.each( p, function( i, obj ){
$( obj )
.attr( "class", "highlightable" );
obj.innerHTML = "<span>" + obj.innerHTML.split(" ").join( "</span> <span>" ) + "</span>";
});
$( ".highlightable span", "#myDiv" )
.on( "click", function(){
$( "span", $( this ).parent() )
.css( "background-color", "");
$( this )
.css( "background-color", "#ffff45" );
});
});
</script>
</head>
<body>
<div id="myDiv">
<p>Here is a sentence.</p>
<p>Here is another sentence.</p>
<p>Here is yet another sentence.</p>
</div>
</body>
</html>
Related
I want to check a condition in which whether the user selection has any two or more div or paragraph sibling in it.
am breaking my head on this am new to dom stuffs. Anybody came across such situation before or somebody can help me with the algorithm or logic to achieve this . any help is highly appreciated.
Whatever you asked here, please share code snippet. Here i make for you example to achieve that. Since you did't post any code, then i'm not sure the example provided meet the expectations.
HTML
<div id="a">Click me
<p></p>
<div></div>
<div></div>
</div>
JS
$('#a').on('click', function(){
var div = $(this).children('div').length;
var p = $(this).children('p').length;
if(div >= 2)
{
alert('div exists : ' + div);
//do your stuff here
}
else
{
alert('i have div below than two');
//do your stuff here if below than two
}
//same goes with p
});
DEMO
I have a contenteditable div. I am trying to hunt for the word "design" and wrap it in a span element.
HTML:
<div id="text"></div>
JavaScript:
$("#text").prop("contenteditable", true);
var string = $("#text")[0].innerHTML;
$("#text").innerHTML = string.replace("design", "<span>design</span>");
This doesn't work. My first guess would be that its because the script runs once, so by the time I type "design" it doesn't catch it. So, I tried putting it in a setInterval, but that didn't work either. JSFiddle
Any help would be much appreciated.
$("#text").prop("contenteditable", true).keypress(function(e){
$(this).html(function(i,html){return html.replace(/<span>design</span>/g,"design").replace(/design/g, "<span>design</span>")});
});
I didn't test, but hopefully it'll work.
You can do it like this
$('#text').prop("contenteditable", true).on('keyup change',function(){
var $el = $(this);
$el.find('span').contents().unwrap(); // remove existing spans around "design"
$el.html(function(i,v){
return v.replace(/\bdesign\b/gi, "<span>design</span>") // wrap design with spans
});
setEndOfContenteditable(this); // reset the cursor to the end
});
FIDDLE
the setEndOfContenteditable function was taken from this SO Answer - it sets the cursor back to the end of the text
https://stackoverflow.com/a/3866442/1385672
I'm working on a simple browser plug in to replace 1 word with another but with the addition of an anchor for a small tooltip/pop up. I have this actually working already but my problem is, if the word to be replaced is within an anchor already, then my </a> closes the already open <a>.
So I need some help with how to only replace a word, as long as its not within an open anchor tag. A follow up to this is to also make sure the target word isn't in an <input> or other inappropriate tags.
My code is:
var regex = new RegExp("\\b" + nativeWord + "\\b", "igm");
var body = $('body');
body.each(function() {
$(this).html(
$(this).html().replace(regex, function() {
counter++;
if(counter > wordSwapLimit) {
return nativeWord;
} else {
return "<a class=\"tooltip\" href=\"#\">" + studyWord + "<span class=\"classic\">" + nativeWord + "</span></a>";
}
})
);
});
I'm suspecting that I might need to write a more complex RegExp but this is my first outing with it so any suggestions would be greatly appreciated!
Update
So I have a test page I work with to see how my code works.
This is part of the original HTML from the page:
<p id="changeArea"> I love chicken but I hate beef. </p>
But with the code shown above and swapping 'chicken' for 'kip', this gets changed to:
<p id="changeArea"> I kip<span class="classic">chicken</span>.com">love <a class="tooltip" href="#">kip<span class="classic">chicken</span></a> but I hate beef. </p>
If I have no anchors around what I am swapping, then it works perfectly and I get a nice rollover tooltip.
Thanks again for your help!
Update 2
As requested, here are 'unprocessed' examples that my plugin might come across when swapping 'chicken' for 'kip':
<p id="changeArea"> I love chicken but I hate beef. </p>
<p id="changeArea2"> Chickenpie? pie-chicken. </p>
What I'm hoping for is the following:
<p id="changeArea"> I love chicken but I hate beef. </p>
<p id="changeArea2"> Chickenpie? pie-<a class="tooltip" href="#">kip<span class="classic">chicken</span></a>. </p>
As you can see in the first line, the html code is left alone, as is text for the word, because it is within a URL and so would break it if my tooltip anchor was put in.
The second line ignores 'chickenpie' because I only want whole word swapping, as per my existing regexp. 'pie-chicken' does get swapped though. This line is processed correctly with existing code.
I'm literally just looking to add additional rules to my existing code that say 'dont replace code and dont replace if within an anchor open tag.
Since you're in JavaScript, you already have access to the DOM. Why would you try using Regexes???
Just iterate through text nodes, ignoring existing links:
(function(elem) {
var recurse = arguments.callee, nodes = elem.childNodes, l = nodes.length, i, t,
getLink = function() {
var a = document.createElement('a'), s = document.createElement('span');
a.href = "#";
a.className = "tooltip";
a.appendChild(document.createTextNode(studyWord));
s.className = "classic";
s.appendChild(document.createTextNode(nativeWord));
a.appendChild(s);
return a;
};
for( i=0; i<l; i++) {
switch(nodes[i].nodeType) {
case 1:
// element - recurse, but not if already a link
if( nodes[i].nodeName != "A") recurse(nodes[i]);
break;
case 3:
// text node, look for keyword
t = nodes[i].nodeValue.search(new RegExp("\\b"+nativeWord+"\\b","i"));
if( t > -1) {
// found it! now to hack around...
t = nodes[i].splitText(t);
t.splitText(nativeWord.length);
t.parentNode.replaceChild(t,getLink());
}
break;
}
}
})(document.body);
Please note: This approach uses Vanilla JS. It is up to you to ensure it is run after the page is loaded. This may be done either by:
Adding the defer attribute to the script, if it is external
Putting the script right before the </body> (or at least after all the content you wish to affect)
Wrapping it in a window.onload handler, or even $(function() {...}) since you're already using jQuery.
I am looking around on the web but I am not finding anything useful. :(
I am having a problem that I don't understand. I am sure that I am doing something wrong, but I don't know well the syntax of jQuery to understand what is that I am not doing right.
I am using animations with JS and CSS 3, and I am having troubles with empty spaces between the words, and to solve this problems I have to find a way to substitute chars inside a string of text with something else. Like an empty space with a , or as a test that I was trying to do a "n" with a "xxxxx".
What I think that I am doing is:
when the page is loaded
Modify the string of any paragraph with the class .fancy-title that contains "n" with a text "xxxxx"
So:
$(document).ready(function(){
for(i=0; i< myLength+1; i++){
var charCheck = $(".fancy-title").text()[i];
if(charCheck == "n"){
charCheck.replace("n", "xxxxxxxx");
}
}
});
But I receive an error that it said:
charCheck.replace("n", "xxxxxxxx"); it is not a function
I am using jquery
and other scripts that are based on jquery to make animations, rotation and scaling... and they are all in the HEAD with jquery first to load.
What am I doing wrong? Manipulation in jQuery does it need a specific .js extension? I understood that was in the basic capability of jQuery, and looking at other examples all creates me the same kind of error.
I even tried
if(charCheck == "n"){
$(".fancy-title").text()[i] == " "
}
But simply the modification it is not applied on the page. I tried with innerHTML as well :(
I feel so incompetent...
Thank you in advance for any help.
$(document).ready(function(){
$(".fancy-title").each(function(i){
var text = $(this).html();
$(this).html(text.replace(/ /g, " "));
})
});
You have no problem with the replace part :).
$(document).ready(function(){
$(".fancy-title").each(function () { //for all the elements with class 'fancy-title'
var s=$(this).text(); //get text
$(this).text(s.replace(/n/g, 'xxxxxxxx')); //set text to the replaced version
});
});
Just a quick example, hope it works.
Have you tried the css style white-space:pre instead of replacing ' ' with ' '?
http://de.selfhtml.org/css/eigenschaften/ausrichtung.htm#white_space
It seems you are trying to replace a single character with a new string.
You might be able to get the right result by dropping the iteration and simply call .replace on the jQuery-object.
Let's say I have the following...
<div id="text">Some text</div>
When my mouse goes over Some, Some will be returned and the same with text.
Is this possible without putting each node into it's own element?
That is pretty much impossible. Since in Javascript you just know that you are hovering over an element (div and/or a textnode in this case), you can't know which word is hovered just like that.
Maybe with alot of effort and some geeky hacks on offsets and/or event.pageX/Y, but I would go for the solution you mentioned yourself, wrapping each word into its own element.
var $text = $('#text');
$text.html($text.text().replace(/\b(\w+)\b/g, "<span class='word'>$1</span>"));
$('.word').bind('mouseenter', function(e){
alert($(this).text());
});
This isn't possible without either wrapping the two nodes in elements or somehow determining the exact position of the text nodes and then binding to some parent's click event. The former option is simpler. Just wrap them in spans:
<div id="text"><span>Some<span> <span>text</span></div>
Or, if you need to wrap them all automatically:
jQuery.fn.getTextNodes = function(){
return this.not('script').contents().map(function(){
var n = this.nodeType;
return n === 3 ? this : n === 1 ? jQuery(this).getTextNodes().get() : [];
});
};
jQuery('#text').getTextNodes().replaceWith(function(){
return jQuery(this.data.replace(/\S+/g, '<span>$&</span>')).filter('span').click(function(){
alert('You just clicked on the word: ' + jQuery.text([this]));
}).end();
});
Demo: http://jsfiddle.net/PYKqM/