I am trying to search a word which is initial caps but matchCase search option is not working as expected. below is my code :
textToHighlight = "Deed";
var rangeCol = para.search(textToHighlight, { matchCase: true});
Paragraph Text : Seller shall convey title to the Property to Buyer by grant deed in the form of letter("Deed").
It always returns first instance of deed which is non caps.
Thanks
See the snippet below, it's working as expected. Are you trying to do something else?
let para = "[...] to Buyer by grant deed in the form of letter"
textToHighlight = "Deed";
var rangeCol = para.search(textToHighlight, { matchCase: true});
console.log(rangeCol); // -1, not found
para = "[...] to Buyer by grant Deed in the form of letter"
textToHighlight = "Deed";
var rangeCol = para.search(textToHighlight, { matchCase: true});
console.log(rangeCol); // 24, found
In terms of highlighting the text with vanilla JavaScript, I've come up with the following:
let _text = text.innerText;
button.addEventListener("click", () => {
text.innerText = _text;
let textToHighlight = input.value;
var rangeCol = text.innerText.search(textToHighlight, {
matchCase: true,
});
let node = text.childNodes[0];
let spaces = node.textContent.match(/^\s*/)[0].length;
let range = new Range();
range.setStart(node, spaces + rangeCol);
range.setEnd(node, spaces + rangeCol + textToHighlight.length);
const mark = document.createElement("mark");
range.surroundContents(mark);
});
#text {
margin: 10px 0;
}
mark {
background-color: yellow;
}
<div id="text">
Seller shall convey title to the Property to Buyer by grant deed in the form of letter("Deed").
</div>
<input id="input" type="text" />
<button id="button">Highlight</button>
Related
I have a basic word scramble game (not styled yet), that works fine on an HTML page.
When I bring that exact code over to a blogger post it stops working.
I can enter text into the text field but on pressing the "Check Word" button it throws back a "Please enter the word to check" even though I have entered a word to check.
Everything else seems to work correctly on blogger (Refresh button, counter, hint, etc.)
The code is as follows...
let words = [
{
word: "addition",
hint: "The process of adding numbers"
},
{
word: "meeting",
hint: "Event in which people come together"
},
{
word: "number",
hint: "Math symbol used for counting"
},
{
word: "exchange",
hint: "The act of trading"
},
]
const wordText = document.querySelector(".word"),
hintText = document.querySelector(".hint span"),
timeText = document.querySelector(".time b"),
inputField = document.querySelector("input"),
refreshBtn = document.querySelector(".refresh-word"),
checkBtn = document.querySelector(".check-word");
let correctWord, timer;
const initTimer = maxTime => {
clearInterval(timer);
timer = setInterval(() => {
if(maxTime > 0) {
maxTime--;
return timeText.innerText = maxTime;
}
alert(`Time off! ${correctWord.toUpperCase()} was the correct word`);
initGame();
}, 1000);
}
const initGame = () => {
initTimer(20);
let randomObj = words[Math.floor(Math.random() * words.length)];
let wordArray = randomObj.word.split("");
for (let i = wordArray.length - 1; i > 0; i--) {
let j = Math.floor(Math.random() * (i + 1));
[wordArray[i], wordArray[j]] = [wordArray[j], wordArray[i]];
}
wordText.innerText = wordArray.join("");
hintText.innerText = randomObj.hint;
correctWord = randomObj.word.toLowerCase();;
inputField.value = "";
inputField.setAttribute("maxlength", correctWord.length);
}
initGame();
const checkWord = () => {
let userWord = inputField.value.toLowerCase();
if(!userWord) return alert("Please enter the word to check!");
if(userWord !== correctWord) return alert(`Oops! ${userWord} is not a correct word`);
alert(`Congrats! ${correctWord.toUpperCase()} is the correct word`);
initGame();
}
refreshBtn.addEventListener("click", initGame);
checkBtn.addEventListener("click", checkWord);
<div class="Gamecontainer">
<h2>Word Scramble</h2>
<div class="content">
<p class="word"></p>
<div class="details">
<p class="hint">Hint: <span></span></p>
<p class="time">Time Left: <span><b>20</b>s</span></p>
</div>
<input spellcheck="false" type="text" />
<div class="buttons">
<button class="refresh-word">Refresh Word</button>
<button class="check-word">Check Word</button>
</div>
</div>
</div>
Any suggestions?
This is because you use querySelector("input") which selects first found input element on the page. Blogger post has multiple input elements therefore your code selects a wrong element. Use IDs or classes to better identify your html elements.
For example you can narrow the query to your html part by using:
inputField = document.querySelector(".Gamecontainer input")
I am creating a quiz game, but I only want the user to be able to submit if all of the input fields are filled out using only JavaScript. I saw other similar questions on StackOverflow that used jquery, but I want to only use JavaScript.
html (Django template)
<h1 class="all_headings" id="quiz_title">{{ quiz.title }}</h1>
<h4 class="quiz_description">By: {{ quiz.user }}</h4>
<h4 class="quiz_description">Created On: {{ quiz.date_and_time }}</h4>
<br>
{% for q in quiz_questions %}
<h3 class="quiz_questions">{{q.1}}
<input type="text" id="id_question{{q.0}}" class="input">
</h3>
<div id="div{{ q.0 }}"></div>
<br>
<br>
{% endfor %}
<input type="submit" value="Submit" class="button quiz_button" id="{{ quiz.id }}">
<h2 id="score"></h2>
<input type="hidden" id="user_id" value="{{ request.user.id }}">
<input type="hidden" id="user_points" value="{{ request.user.points }}">
{% if request.user.is_anonymous %}
<h3 class="all_headings">Join now to earn 10 points for every question you answer correctly!</h3>
{% endif %}
Update: I added the entire Django template code so that you can see what is going on.
current JavaScript
function bindText(e){
const required = [...document.querySelectorAll(".inputs")];
required.forEach(input => input.oninput = checkText);
}
function checkText(e){
const required = [...document.querySelectorAll(".inputs")];
const button = document.getElementsByClassName(".quiz_button");
button.disabled = !required.every(input => input.value.length > 0);
}
document.addEventListener('DOMContentLoaded', function(){
//gets all the quiz_buttons enableChecking();
const quizButton = document.querySelectorAll('.quiz_button');
for (const button of quizButton){
button.addEventListener('click', (event) => check_quiz(event.target.id));
}
})
function check_quiz(id){
console.log("button is clicked");
//get answers
let response1 = document.getElementById('id_question1').value.toUpperCase().replace(/\s/g, "");
let response2 = document.getElementById('id_question2').value.toUpperCase().replace(/\s/g, "");
//repeats 8 more times
//get divs
let div1 = document.getElementById('div1');
let div2 = document.getElementById('div2');
//repeats 8 more times
var correctAnswers = 0;
//get quiz
fetch(`/check/${id}`)
.then(response => response.json())
.then(quiz => {
rightM = "You got this correct. Great job!";
//checking if the answers are right
//#1
let answ1 = quiz.answer1.toUpperCase().replace(/\s/g, "");
if(answ1 === response1){
div1.innerHTML = rightM;
div1.classList.add("correct");
correctAnswers++;
} else{
div1.innerHTML = `The correct answer is ${quiz.answer1}. Nice try!`;
div1.classList.add("incorrect");
}
//#2
let answ2 = quiz.answer1.toUpperCase().replace(/\s/g, "");
if(answ2 === response2){
div2.innerHTML = rightM;
div2.classList.add("correct");
correctAnswers++;
} else{
div2.innerHTML = `The correct answer is ${quiz.answer2}. Nice try!`;
div2.classList.add("incorrect");
}
//repeats 8 more times
console.log(correctAnswers)
//display score
let score = document.getElementById("score");
score.innerHTML = `Your score is ${correctAnswers}. Great job! :)`;
score.classList.add("score_style");
//points
let newPoints = correctAnswers * 10;
let currentUser = parseInt(document.getElementById("user_id").value);
let currentPoints = parseInt(document.getElementById("user_points").value);
let numOfPoints = currentPoints + newPoints;
console.log(numOfPoints);
fetch(`/points/${currentUser}`,{
method: "PUT",
body: JSON.stringify({
points: numOfPoints
})
})
})
}
I updated this with my current code and fixed all the errors from Update 2 of the answer, but the submit button still does not work when it is pressed.
Update 2
You need to go to JSHint and debug your code frequently. In Update 2 I have corrected as much as I care to. Before I can help you any further, you need to address the issues marked with a β.
Mark
Description
Corrected
β
There are 12 typos, even if there was only one β it would FUBAR whatever follows it.
Yes
β
There are 5 questionable areas which will most likely need to be addressed because it may cause problems later on, or is pointless, etc.
No
π©
Areas that could be modified to be more efficient, succinct, etc.
No
β
Areas that are modified to be more efficient, succinct, etc.
Yes
In Example D I placed where my code should go. There's always the chance that none of it may work because of Django. I have no idea, but on the surface it doesn't look debilitating.
Concerning <form>s, they are the the backbone of most interactive webpages but not necessary if you have other means such as fetch(). That is true in your case which means that you don't trigger "submit" events and you don't need a type="submit" unless there's something Django does that I'm ignorant to.
It isn't obvious to me how the HTML layout is exactly. I'm not certain whether there's one button (good) or multiple buttons (a very critical detail to know from the start).
Example D
document.addEventListener('DOMContentLoaded', bindText);
function bindText(e) {
const required = [...document.querySelectorAll(".inputs")];
required.forEach(input => input.oninput = checkText);
}
function checkText(e) {
/*β: = Does not belong at end of line */
const required = [...document.querySelectorAll(".inputs")];
/*β: `${id}` NOT "${id}"|BUT `${id}` === id */
/*β: Is id declared elsewhere? If not, use it's actual "idOfButton".
If you have multiple submit buttons then you should reassess
your strategy.
*/
const button = document.getElementById(id);
/*β: = missing */
button.disabled = !required.every(input => input.value.length > 0);
}
/*β: '...' or "..." NOT "...' */
document.addEventListener('DOMContentLoaded', function() {
/*β: = : ' missing */
const quizButton = document.querySelectorAll('.quiz_button');
for (const button of quizButton) {
/*β: In event handlers/listeners if it is a named function that's
defined separately, remove the ()
*/
button.addEventListener('click', (event) => checkQuiz);
}
});
/*β: Event handlers pass the (e)vent object by default */
function checkQuiz(e) {
const id = e.target.id;
/*β: / missing a \ needs to be escaped: \\ */
/*β: response* number 1 NOT letter l */
let response1 = document.getElementById('id_question1').value.toUpperCase().replace(/\\/g, "");
/*β: / missing a \ needs to be escaped: \\ */
let response2 = document.getElementById('id_question2').value.toUpperCase().replace(/\\/g, "");
/*β: Are sure there's a <div id="div">? */
let div1 = document.getElementById('div');
/*β: '...' or '..." NOT '..." */
let div2 = document.getElementById('div2');
/*β: var hoists to be global use let or const. In this case let
is the best choice
*/
var correctAnswers = 0;
fetch(`/check/${id}`)
.then(response => response.json())
.then(quiz => {
/*β: Is rightM declared elsewhere? If not,
use let or const
*/
rightM = "You got this correct. Great job!";
let answ1 = quiz.answer1.toUpperCase().replace(/\s/g, "");
if (answ1 === response1) {
div1.innerHTML = rightM;
div1.classList.add("correct");
correctAnswers++;
} else {
div1.innerHTML = `The correct answer is ${quiz.answer1}. Nice try!`;
div1.classList.add("incorrect");
}
let answ2 = quiz.answer1.toUpperCase().replace(/\s/g, "");
if (answ2 === response2) {
div2.innerHTML = rightM;
div2.classList.add("correct");
correctAnswers++;
} else {
div2.innerHTML = `The correct answer is ${quiz.answer2}. Nice try!`;
div2.classList.add("incorrect");
}
/*π©: DRY = Don't Reapeat Yourself. Iterate through quiz.answer1 with
a loop or an array method.
*/
//this repeats 8 more times
console.log(correctAnswers);
let score = document.getElementById("score");
/*β: ` missing */
/*β: $' '{ space between $ and { breaks interpolation */
score.innerHTML = `
Your score is ${correctAnswers}.Great job!:)`;
score.classList.add("score_style");
/*β: is newPoint declare elsewhere? If not use let or const */
newPoints = correctAnswers * 10;
let currentUser = parseInt(document.getElementById("user_id").value);
let currentPoints = parseInt(document.getElementById("user_points").value);
let numOfPoints = currentPoints + newPoints;
/*β: ` missing*/
fetch(`/points/${currentUser}`, {
method: "PUT",
body: JSON.stringify({
points: numOfPoints
})
});
});
}
Update 1
As requested examples B and C include JavaScript that'll enable a disabled <button> when all <input>have text.
If you have a <form> wrapped around everything (which is very simple to do if you don't), add required to all of the <input> -- that'll prevent the <form> from being submitted should there be any blank <input>s (see Example A).
Example A - Has no JavaScript or CSS -- just HTML. The <button> isn't disabled, but the <form> will not allow itself to be submitted if any <input> is blank.
Example B - Has a <form> and uses HTMLFormElement and HTMLFormControlsCollection interfaces which facilitates the use of form controls.
Example C - Is a less elegant solution involving standard DOM manipulation.
Note that in Example B the event handler is bound to the <form> and that it is shared by each <input>. Should any form control be added to the <form> later on, the event handling will apply to it as well. In Example C each <input> has the input bound to them individually. Should any form control be added later on they'll need to have their own event handler/listener attached after they have been created.
Examples B and C have commented details
Example A
<form>
<input required value='XXXXXXXXXXX'><br>
<input required value='XXXXXXXXXXX'><br>
<input required placeholder='Enter text here'><br>
<button>Submit</button>
</form>
Example B
// Reference the <form>
const F = document.forms[0];
// Reference all form controls
const fc = F.elements;
// Collect all name='required' into a HTMLCollection, convert it into an array
const required = [...fc.required];
// Reference the <button>
const done = fc.done;
/**
* Bind input event to <form>...
* ...enable the <button> if .every() <input> has a value
*/
F.oninput = e =>
done.disabled = !required.every(input => input.value.length > 0);
<form>
<input name='required' value='XXXXXXXXXXX'><br>
<input name='required' value='XXXXXXXXXXX'><br>
<input name='required' placeholder='Enter text here'><br>
<button id='done' disabled>Done</button>
</form>
Example C
// Collect all .required into a NodeList, convert it into an array
const required = [...document.querySelectorAll('.required')];
// Reference the <button>
const done = document.getElementById('done');
/**
* Bind the input event to each <input>...
* ...<button> is enabled if .every() <input> has a value
*/
required.forEach(input => input.oninput = e =>
done.disabled = !required.every(input => input.value.length > 0));
<form>
<input class='required' value='XXXXXXXXXXX'><br>
<input class='required' value='XXXXXXXXXXX'><br>
<input class='required' placeholder='Enter text here'><br>
<button id='done' disabled>Done</button>
</form>
What I am trying to do is build the ability to add tagging with a text editor #user and populate a list of users they can select from and it will insert that into the editor. I want to grab all the text before the # when it is typed up to the first space so that I can distinguish if the user is trying to type an email or wanting to add a tag. I know I can just split up the string from # and detect that, but I am having a hard time knowing where to start to get that text to begin with.
Any help would be great.
$(document).on('keyup', '.element', function(e) {
if (e.keyCode == 50) {
//get text here
}
})
Intro
Here is a sample of something that might cover your needs.
However, what I did was indeed of detecting the #, I detected the space.
Once the space was clicked, I went back to find the # .
JSFiddle: https://jsfiddle.net/2uqgorka/35/
JS
let output = document.getElementById("output");
let result = document.getElementById("result");
input.addEventListener('keyup', logKey);
function logKey(e) {
console.log(e);
output.innerHTML += ` ${e.code} + ${e.keyCode}`;
if (e.keyCode == 32) { //Detect a space
let startPos = e.target.selectionStart;
let endPos = e.target.selectionEnd;
//alert(startPos + ", " + endPos);
if(startPos == endPos){
console.log("full input:"+e.target.value);
let textUpToPosition =e.target.value.substring(0,endPos-1);
console.log("textUpToPosition:"+textUpToPosition);
let previousAt = textUpToPosition.lastIndexOf("#");
let previousSpace = textUpToPosition.lastIndexOf(" ");
console.log("previousAt:"+previousAt);
console.log("previousSpace:"+previousSpace);
if(previousSpace < previousAt){
let resultText = textUpToPosition.substring((previousAt));
result.innerHTML = resultText;
}
}
}
}
HTML
<textarea id="input">
#Someone test
</textarea>
<hr>
KeyStrikes<br>
<div id="output">
</div>
<hr>
Result<br>
<div id="result">
</div>
I'm using a search-function for a documentation site which upon selection of search hit shows page with text highlighted (just as a pdf-reader or netbeans would do).
To achive the highlight i use javascript with:
function searchHighlight(searchTxt) {
var target = $('#page').html();
var re = new RegExp(searchTxt, 'gi');
target = target.replace(
re,
'<span class="high">' + searchTxt + '</span>'
);
$('#page').html(target);
}
Problem / Question:
Since page incudes images with filenames based on md5, some searches messes up the image src.
Searching on "1000" will distort the
<img src="53451000abababababa---.jpg"
to
<img src="5334<span class="hl">1000</span>abababab--.jpg">
Is it possible to solve this with regexp, somehow excluding anything anjcent to ".jpg"?
Or would it be possible to, before highligting replace the images with placeholders, and after replace revert back to src?
Example:
replace all <img *> with {{I-01}}, {{I-02}} etc and keep the real src in a var.
Do the replace above.
Revert back from {{I-01}} to the <img src=".."/>
DOM-manipulation is of course an option, but I figure this could be done with regexp somehow, however, my regexp skills are lacking badly.
UPDATE
This code works for me now:
function searchHighlight(searchTxt) {
var stack = new Array();
var stackPtr = 0;
var target = $('#page').html();
//pre
target = target.replace(/<img.+?>/gi,function(match) {
stack[stackPtr] = match;
return '{{im' + (stackPtr++) + '}}';
});
//replace
var re = new RegExp(searchTxt, 'gi');
target = target.replace(re,'<span class="high">' + searchTxt + '</span>');
//post
stackPtr = 0;
target = target.replace(/{{im.+?}}/gi,function(match) {
return stack[stackPtr++];
});
$('#page').html(target);
}
One approach would be to create an array of all possible valid search terms. Set the terms as .textContent of <span> elements within #page parent element.
At searchHighlight function check if searchTxt matches an element within array. If searchTxt matches an element of array, select span element using index of matched array element, toggle "high" .className at matched #page span element, else notify user that searchTxt does not match any valid search terms.
$(function() {
var words = [];
var input = $("input[type=text]");
var button = $("input[type=button][value=Search]");
var reset = $("input[type=button][value=Reset]");
var label = $("label");
var page = $("#page");
var contents = $("h1, p", page).contents()
.filter(function() {
return this.nodeType === 3 && /\w+/.test(this.nodeValue)
}).map(function(i, text) {
var span = text.nodeValue.split(/\s/).filter(Boolean)
.map(function(word, index) {
words.push(word);
return "<span>" + word + "</span> "
});
$(text.parentElement).find(text).replaceWith(span);
})
var spans = $("span", page);
button.on("click", function(event) {
spans.removeClass("high");
label.html("");
if (input.val().length && /\w+/.test(input.val())) {
var terms = input.val().match(/\w+/g);
var indexes = $.map(terms, function(term) {
var search = $.map(words, function(word, index) {
return word.toLowerCase().indexOf(term.toLowerCase()) > -1 && index
}).filter(Boolean);
return search
});
if (indexes.length) {
$.each(indexes, function(_, index) {
spans.eq(index).addClass("high")
})
} else {
label.html("Search term <em>" + input.val() + "</em> not found.");
}
}
});
reset.on("click", function(event) {
spans.removeClass("high");
input.val("");
label.html("");
})
})
.high {
background-color: #caf;
}
label em {
font-weight: bold;
background-color: darkorange;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<input type="text" />
<input type="button" value="Search" />
<input type="button" value="Reset" />
<label></label>
<div id="page" style="max-width:500px;border:1px solid #ccc;">
<h1 style="margin:0px;">test of replace</h1>
<p>After Luke comes to Dagobah, Yoda initially withholds his true identity. Heβs trying to get a sense of who Luke is as a person; Yoda understands that thereβs a lot at risk in training Luke to be a Jedi, especially considering what happened with his
father.
<img style="float:right;" width="200" src="http://a.dilcdn.com/bl/wp-content/uploads/sites/6/2013/11/04-400x225.jpg">And Yoda is not impressed β Luke is impatient and selfish. With βAdventure. Excitement. A Jedi craves not these things,β the Jedi Master makes clear that Luke must understand the significance and meaning of the journey he thinks he wants to make.
Itβs an important lesson for Luke and for audiences, because when Luke faces Vader at the filmβs climax, we see the stakes involved in the life of a Jedi</p>
<p>Now Yoda-search works, however a search on "sites" will break the image-link. (Yes, I know this implementation isn't perfect but I'm dealing with reality)</p>
</div>
When search any text on search box, it can be find and highlighted the correct text, but when search next/new text, it's unable to find the next/new text, it's not working when search again, i'm unable to find the issue. The JS below.
JS
$('button#search').click(function() {
var page = $('#ray-all_text');
var pageText = page.html().replace("<span>", "").replace("</span>");
var searchedText = $('#searchfor').val();
var theRegEx = new RegExp("(" + searchedText + ")", "igm");
var newHtml = pageText.replace(theRegEx, "<span>$1</span>");
page.html(newHtml);
$('html, body').animate({
scrollTop: $("#ray-all_text span").offset().top }, 2000);
});
HTML
<div class="ray-search">
<div class="field" id="ray-search-form">
<input type="text" id="searchfor" placeholder="what are you searching for?" />
<button type="button" id="search">Press to Find!</button>
</div>
</div>
<article id="ray-all_text">
<p>
This manual and web site, all information and data and photos contained herein, are the s...
</p>
</article>
Please check the live Example: https://jsfiddle.net/gaezs6s8
Why is this happening? Is there a solution?
My suggestion is to make a few validations before change all the .html() inside the text you want to avoid unexpected behaviors and improve the functionality.
First make a validation to avoid the 'space' as the first value on the input, this will let us later check if the input has a real value inside.
$('body').on('keydown', '#searchfor', function(e) {
if (e.which === 32 && e.target.selectionStart === 0) {
return false;
}
});
Code from this answer
Now Please check the comments on your code:
//Create some vars to later check for:
//['text you are searching', 'number of highlights','actual highlight']
var searching,
limitsearch,
countsearch;
$('button#search').click(function() {
var searchedText = $('#searchfor').val();
var page = $('#ray-all_text');
//Now check if the text on input is valid
if (searchedText != "") {
//If the actual text on the input is different from the prev search
if(searching != searchedText) {
page.find('span').contents().unwrap();
var pageText = page.html();
var theRegEx = new RegExp("(" + searchedText + ")", "igm");
var newHtml = pageText.replace(theRegEx, "<span>$1</span>");
page.html(newHtml);
//Set your variables to the actual search
searching = searchedText;
limitsearch = page.find('span').length;
countsearch=0;
} else {
//If it's the same of the prev search then move to next item instead of repaint html
countsearch<limitsearch-1 ? countsearch++ : countsearch=0;
console.log(countsearch+'---'+limitsearch)
}
//Go to target search
$('body').animate({
scrollTop: $("#ray-all_text span").eq(countsearch).offset().top - 50},
200);
} else {
alert('empty search')
}
});
JqueryDemo