JS querySelector + ID with dynamic values - javascript

Im trying to make a simple quiz with a dynamic questions using Jinja (so a bit of python as well) + some SQL + JS.
Since im quite new to this, I was trying to do a simple "click here -> change color to green if your answer is the right one"
Here's the thing: to not complicate things, i want every answer to change the color to red (if wrong) or green (if right). Right know, thanks to this thread Javascript getElementById based on a partial string i manage to create a function that turns the right answer to green wih the code, no matter where the user clicks (as long its inside the question box answers):
document.querySelector('[ id$="{{ question.correct_answer }}"]').style.backgroundColor="rgb(0, 221, 135)";
I thought i could do something like "id$!=" and that would solve my problem, but that didnt work. So i tried to search for other stuff like the :not or not() selectors, but that showed me a lot of jquery stuff, which im not studying/learning right now. So, is there any way to write:
"if the id$ does not match the value {{ question.correct_answer }}, turn red" in plain JS?
Some important stuff about the code:
All answers have id="answer_a", "answer_b" etc.
That matches the way i save que "correct_answer" in the database, which comes exactly like the ID (so if the correct_answer is answer_d, i can call "{{ question.correct_answer }}" and that will always turn D into GREEN;
my HTML looks like <div class=question_answer id="answer_d" onclick="selecResposta()"> {{ question.answer_d }} </div> <br>. These are inside a DIV called "question_options" which i can also put the "onclick" function and everything works the same.
I can provide more information if necessary.
Thanks a lot for the help and sorry if this is something easy to solve. Any guidance (if you dont wanna say the answer) is quite welcome as well.
UPDATE:
Thanks to #connexo and #Vijay Hardaha, i manage to mix both answers and create a code that helped me. It might not be pretty, but its doing what i want so its perfect. Here's the solution:
html part:
<div class=question_answer data-answer="answer_a"> {{ question.answer_a }} </div> <br>
etc.etc
js:
function selecRightAnswer() {
document.querySelector("[data-answer={{ question.correct_answer }}]").style.backgroundColor="rgb(0, 221, 135)";
}
function selectWrongAnswer() {
const elements = document.querySelectorAll("div.question_answer:not([data-answer={{ question.correct_answer }}])");
elements.forEach(function (element) {
element.style.backgroundColor = "red";
});
}

Selects div with class question_answer when div has id=answer_a with an exact match.
document.querySelector("div.question_answer[id=answer_a]");
Selects div with class question_answer when div doesn't have id=answer_a with an exact match.
document.querySelector("div.question_answer:not([id=answer_a])");
document.querySelector will only selector first matched div. so if you have to work with all
unmatched with answer_a then you need to use document.querySelectorAll
and then you'll have to loop reach element and work with each element inside the loop.
Example
const elements = document.querySelectorAll(".box");
elements.forEach(function (element) {
element.style.color = "green";
});

Related

by.buttonText('') does not return empty texted buttons as protractor locator

I have to test an application that has some buttons with no text, only graphics (these are < and > buttons on a datepicker, for instance, not in my control).
If I try to find these by:
elm = element.all(by.buttonText(''));
backButton = elm.get(1);
I get no immediate error, but if I say backButton.click() I get:
Error: Index out of bound. Trying to access element at index: 1, but there are only 0 elements that match locator by.buttonText("").
On the other hand if I say
element.all(by.tagName("BUTTON")).filter(function (elem) {
return elem.getText().then(function (text) {
//find the empties
return !(text);
//
});
}).then(function (EmptyButtons) {
EmptyButtons[1].click()
I get a fun error suggesting I found one that I clicked did not pop up yet:
JavascriptError: Element is not currently visible and may not be manipulated (status: 11)
but that is on me to figure out. If I track the text as it comes through the filter, I find there are multiple text-less buttons placed in the array.
I am sorry I do not yet have the skills to generate and test a small example, but I hope perhaps some helpful reader does.
Anyway, I suppose it is possible I am missing something subtle about promise resolution or that the desired behavior is you cannot find a textless button by looking for empty text. I have a workaround, too (actually multiple ones - see my comment below). But I would welcome some clarification here if someone can give it.
Best,
Jeremy Kahan
p.s. I do not see this as a duplicate of "How to click a button with no text in protractor" inasmuch as that wrestles with distinguishing textless buttons, and my question is not how to find and distinguish them, but why searching for text '' does not find them. [My code even suggests how that could have been or could still be implemented, I think.]
From what I understand, you have good workarounds and ways to solve the problem without using by.buttonText and only need an explanation why by.buttonText('') does not work.
If we look into how the by.buttonText is implemented, we would see it first finds only the following "button" variations:
button, input[type="button"], input[type="submit"]
Which means that if the element you are looking for is not the button or appropriate input element, you would not get it matched by the locator.
And, judging by the workaround, the desired elements in your case are actually span elements.
Aside, from that, matching by the empty "button text" should work.
Tested on the datepicker used in our internal application under test - worked for me. Here is how the "<" button looks in our case:
<button type="button" class="btn btn-default btn-sm pull-left" ng-click="move(-1)" tabindex="-1" ap-tab-nav-idx="1.2" ap-tab-nav-child="true">
<i class="glyphicon glyphicon-chevron-left"></i>
</button>
And, if I mimic the "by.buttonText" client side script logic in the console, I would get this element passing all the conditions which determine whether to pick an element or not:
> var element = $("button[ng-click='move(-1)']")[0]
> elementText = element.textContent || element.innerText || '';
> elementText.trim() === ""
true

jQuery how to do the following

I've the following question, let say we have a div like this:
These are dynamically formatted divs, with the classes 'row', 'element' and 'isotope-item' are always present. Everything in between can vary per div.
What I want is to the following:
As you see the commmas are no longer there and seperate classes between the commas are now one class.
Anyone any idea?
I already have the following to remove the commas:
$('div.element').each(function () {
var _sCurrClasses = jQuery(this).attr('class');
jQuery(this).attr('class', _sCurrClasses.replace(/,/g, ' '));
});
I would advise doing this backend,but in JavaScript you could:
This will not account for the space in the words though.
You would need to pass then trough separately one by one and replace.
or store them in a data-attribute and format when you need them.
<string>
var classesFormat = classes.replace(/,/g, '');
var classesList = classesFormat.split(" ");
for(String c : classesList)
{
$("#id").addClass(c);
}
</string>
So you could create a data-attribute for each one instead.
Go through each one, format and the add to class.
<div data-id="Microsoft Office," class="test test test">
With the script
$(this).attr("data-id") // will return the string "Microsoft Office,"
or .data() (if you use newer jQuery >= 1.4.3)
$(this).data("id") // will return the Microsoft Office,
And then do your replace after that and addClass.
I don't think classes work like you think they do
the first PICTURE you posted would result in that div having the follwing classes
row
element
Microsoft
Office,
My
SQL,
Page
Rank
isotope-item
Note the , is PART of the class
You want, according to the second PICTURE
row
element
MicrosoftOffice
MySQL
Page
Rank
isotope-item
Removing , is just as you posted ... the problem is, how do you determine which spaces to remove, and which to keep?
(I posted this as an ANSWER, but I KNOW IT IS NOT AN ANSWER)

Create a region on HTML with changing values

I am a beginner in HTML and I want to create a region on a HTML page where the values keep on changing. (For example, if the region showed "56" (integer) before, after pressing of some specific button on the page by the user, the value may change, say "60" (integer) ).
Please note that this integer is to be supplied by external JavaScript.
Efforts I have put:
I have discovered one way of doing this by using the <canvas> tag, defining a region, and then writing on the region. I learnt how to write text on canvas from http://diveintohtml5.info/canvas.html#text
To write again, clear the canvas, by using canvas.width=canvas.width and then write the text again.
My question is, Is there any other (easier) method of doing this apart from the one being mentioned here?
Thank You.
You can normally do it with a div. Here I use the button click function. You can do it with your action. I have use jquery for doing this.
$('.click').click(function() {
var tempText = your_random_value;
// replace the contents of the div with the above text
$('#content-container').html(tempText);
});
You can edit the DOM (Document Object Model) directly with JavaScript (without jQuery).
JavaScript:
var number = 1;
function IncrementNumber() {
document.getElementById('num').innerText = number;
number++;
}
HTML:
<span id="num">0</span>
<input type='button' onclick='IncrementNumber()' value='+'/>
Here is a jsfiddle with an example http://jsfiddle.net/G638z/

JSF Set CSS Style Using Javascript?

I have been looking with no success to see if I can dynamically apply a css style to JSF component or div using javascript. Is this possible.
This is pseudo code
<div style="myJSStyleFunction("#{myBean.value}")"> stuff </div>
And the function would return something like "position:relative;left:25px;"
I've had no luck and maybe it can't be done but would like a second opinion.
Edit:
I'm trying to see if I can keep a separation / reduce the coupling between the presentation/view and the model/controller. This is for indenting commenting or product reviews (to nest replies to comments or reviews). The most I really want to track is an integer on how deep a reply is. First level = 0 second level = 1, and so on. So a comment or product review would be 0 deep, a reply to the comment or review would be 1 and so on.
Then in the EL I wanted to call a javascript function and do something like
<script>
myJSStyleFunction(depth){
if(depth<=5){
var nest=20*depth;
var style="position:relative;left:" + nest + "px;";
return style;
}
}
</script>
And then then say for a third level comment (a reply to a reply) it would look like this:
<div style="position:relative;left:40px;"> stuff </div>
where
#{myBean.value}
evaluates to 2
I suspect like Daniel says I'll have to tightly couple the view but I'd rather not have to. I'd think there has to be a way. But maybe not.
I don't know where there are cleaner solutions for this. However this is one suggestion.
Assume your page looks like below and myBean.getValue() method returns an integer.
<h:form id="frm">
<div style="#{myBean.value}"> div1 </div>
<div style="#{myBean.value}"> div2 </div>
</h:form>
So you can do something like this at 'window.onload'.
<head>
<script>
window.onload = function() {
var childList = document.forms['frm'].childNodes;
for(var i = 0; i < childList.length; i++) {
if(childList[i].nodeName == 'DIV') {
var _div = childList[i];
var depth = _div.getAttribute('style');
_div.setAttribute('style', 'position:relative;left:' +(depth *20)+ 'px;');
}
}
}
</script>
</head>
Note: 1. In above sample code I assume all the DIVs inside the form should be indented.
2. For IE you may need to use _div.style.setAttribute('cssText','position:relative;left:' +(depth *20)+ 'px;')
3. Another solution for your question is using <script> tags immediately after your divs and putting the js part inside them. In this way you don't have to use fake styling style="#{myBean.value}" or window.onload event because you can directly call #{myBean.value} in your script.
I decided to skip the javascript approach and settled on a simpler and I think cleaner method to create the dynamic css classes for my situation. I already capture/calculate the depth value for each comment when it is entered. So I am just returning that value in EL and concatenating it to a 'base name' for the css class like so:
<div class="indent_#{(comment.commentDepth le 5) ? comment.commentDepth : 5}" >
comment comment blah blah blah
</div>
"indent_" is the base name for the css class. So for a 0 level comment it will have a class="indent_0". A reply to that comment will have class="indent_1".
I use the ternary so that if there are lot of replies under a given comment it doesn't indent right off the right hand side of the page. Even though you can keep going deeper, it will only indent up to 5 levels.
For my case at the moment, this is a simpler and cleaner method of adding some dynamically generated css class names. Right now I have to define 6 classes for this in the css file, but perhaps I'll figure out how to nest the boxes but it isn't a priority this works just fine for me for now.

Wrapping a jquery validate span.error with nested divs

Heyo. This is my first stack overflow post because I am stumped and not finding many people who are trying to accomplish the same thing. I've tried using jquery .before(), .after(), and .wrap() to resolve this. I was initially using css :before and :after pseudo-elements, but as that won't work for legacy browsers, I've decided to use jquery.
I already have several forms on several pages with validation working. The error messages vary in length. We were using a static, one size background image on the default span element, so content was bleeding out on longer error messages. I built a flexible rounded corner series of nested divs to allow the error box to grow or shrink dynamically. The html I want to output is:
<div class="errorWrap">
<div class="errorTop"><span></span></div>
<div class="errorContent">
<span class="error">This is an error</span>
</div>
<div class="errorBottom"><span></span></div>
</div>
Here's an example of a solution I tried, but I'm still pretty new to javascript.
$('.error').before('<div class="errorWrap"><div class="errorTop"><span></span></div><div class="errorContent">');
$('.error').after('</div><div class="errorBottom"><span></span></div></div>');
Correct me if I'm wrong, but I think that I have the right idea with the jquery. But it's just kind of sitting there, not in any function being called. So I imagine that since the code isn't re-executing, it just doesn't show up. Is there an appropriate function to wrap this in? I'm certain I'm just not attacking this from the right direction. Any help is super appreciated.
the plugins "before" and "after" dont take html as string. you cannot start a div in one and close it in an other.
Either you take your current html and generate a new html string which you append where you want to or you use the "wrap" plugin http://api.jquery.com/wrap/
Using pure HTML
$(".error").html("<div class='beforeContent'>" + $(".error").html() + "</div>");
Using wrap (http://api.jquery.com/wrap/)
$(".error").wrap("<div class='beforeAndAfter'></div>");
If you want to show an error div after focus out of an input then you have to create it using html/wrap as Luke said and then you have to append it in ot the dom useing
$('.errorWrap').insertAfter('.focusedElement');
But there are other methods available to insert a new element like append/appendTo e.t.c,
I ended up fixing this problem on my own using jquery to create the div and it's nesting on pageload, the divs are generated with an error class that gives display:none. A custom errorPlacement function nests the error in the correct div. Then I used a custom validator highlight function to remove the class that hides the element. Then I used the unhighlight function to re-add the class to re-hide the div.
$(function() {
//Generate the elements and assign attributes
var errorWrap = document.createElement('div');
$(errorWrap).addClass('errorWrap hideError');
var errorTop = document.createElement('div');
$(errorTop).addClass('errorTop');
var topSpan = document.createElement('span');
var errorContent = document.createElement('div');
$(errorContent).addClass('errorContent');
var errorBottom = document.createElement('div');
$(errorBottom).addClass('errorBottom');
var bottomSpan = document.createElement('span');
//Place the elements directly after each dd element
$("dl > dd").append(errorWrap);
$("div.errorWrap").append(errorTop)
.append(errorContent)
.append(errorBottom);
$("div.errorTop").append(topSpan);
$("div.errorBottom").append(bottomSpan);
//Add custom validator defaults
$.validator.setDefaults({
errorPlacement: function(error, element) {
$(element).nextAll('.errorWrap').children('.errorContent').append(error);
},
highlight: function(element) {
$(element).nextAll('.errorWrap').removeClass('hideError');
},
unhighlight: function(element) {
$(element).nextAll('.errorWrap').addClass('hideError');
}
});
}
Although I'm sure this could have been done more shorthand, I really like this technique because I didn't have to update any of my pages that contained forms to get it to work. All of the nested divs are dynamically created by javascript, so I can include a global file to any page with forms and it will just work. Thanks for all who offered suggestions.

Categories

Resources