Jquery selector to "this" return - javascript

I have the following code:
function checkBoxOpenStatus(clickedBox) {
if($("#productsBox").hasClass("boxOpen")) {
closeBox(obj.productsBox.bx, clickedBox);
delay = 200;
} else if($("#fabricationBox").hasClass("boxOpen")) {
closeBox(obj.fabricationBox.bx, clickedBox);
delay = 200;
} else if($("#aboutPBBox").hasClass("boxOpen")) {
closeBox(obj.aboutPBBox.bx, clickedBox);
delay = 200;
} else { delay = 0; }
My goal is to evaluate three boxes, if one of them has class "boxOpen", then I need to close that box, and add a delay to my "openSelectedBox" function (not present above). My question is, can I change the above code to the following: (without loosing the ability to send a specific selector?)
if($("#productsBox" || "#fabricationBox" || "#aboutPBBox").hasClass('boxOpen') {
closeBox(obj.(**WHAT SELECTOR CAN GO HERE??**).bx, clickedBox);
delay = 200;
I know the "or" evaluation works, but I don't know how to use that "or" defined instance to fire the "closeBox" function with the appropriate selection. I would rather learn that take, so please give me an explanation which selector could work there while performing an evaluation like this.

You noticed duplicated code blocks and wanted to optimize them. (Insert I like you meme here).
You can do it this way:
// This will get the 3 elements in a jQuery object and filter the 3 elements to only keep the ones that have the class "boxOpen"
var theOnesWithTheClass = $("#productsBox, #fabricationBox, #aboutPBBox").filter(".boxOpen");
// This will call the closeBox with the id of each element inside the jQuery object. I do it this way just in case there are more than 1 element.
// The jQuery each method will call the function setting the this context to each element inside the jQuery object.
theOnesWithTheClass.each(function() {
closeBox(obj[this.id].bx, clickedBox);
});
// This will set the delay to 200 only if there are elements inside the jQuery object. The ? conditional returns the block before the : if true and the block after it if false.
delay = theOnesWithTheClass.length > 0 ? 200 : 0;

Use a selector that matches all the boxes with the class. If this returns at least 1 element, there's an open box, and you can get this element's ID.
Then you can use another selector to get the ID of the element with the class.
var openBoxes = $("#productBox.boxOpen, #fabricationBox.boxOpen, #aboutPBBox.boxOpen");
if (openBoxes.length > 0) {
var openId = openBoxes.attr("id");
closeBox(obj[openId].bx, clickedBox);
delay = 200;
} else {
delay = 0;
}
It would be a little simpler if you gave all these boxes a common class, instead of having to list all the IDs in the selector.

Related

Detect a button and then press it in JavaScript

I want to make a function that would detect a button on a web page and then click it. But I want it to click a specific item.
function imready()
{
var btn = document.getElementsByClassName('text-xxxs mb-02');
for (var i = 0; i < btn.length; i++)
{
if (btn[i].innerText.indexOf('AK-47') > -1)
{
console.log('runtime');
chrome.runtime.sendMessage({ type: 'dontrun', update: 1 }, function (response) {
});
btn[i].click();
pressok();
}
}
How do I make it so that the var "btn" should equal to document.getElementsbyClassName('x') and also a different className ('y')?
Quoting from https://stackoverflow.com/a/29366682/10450049
getElementsByClassName() returns an HTMLcollection object which is similar to an array but not really an array so you can't call
array methods using the returned value. One hack is to use Array's
prototype methods along with .call()/.apply() to pass the returned
object as the context.
var elems = document.getElementsByClassName("royal") ;
var collapsedElems = document.getElementsByClassName("collapsed");
var earray = Array.prototype.slice.call(elems, 0);
var concatenated = earray.concat.apply(earray, collapsedElems) ;
console.log(concatenated)
Demo Fiddle
As far as i understand your question, you can use document.querySelector('.classX.classY') to select the needed button with both classes.
That works for the case if you only need one button on the page selected, from your code i assume exactly that.

DOM Traversa l Question - Using mutationObserver to insertAdjacentHTML after each node, and to addEventListener to each button

So, I'm using setMutationObserver to insertAdjacentHTML input buttons in to a div class named '._5pcq'. after the end of that class. That class is located inside of a container named '._3576'.
https://greasyfork.org/scripts/12228/code/setMutationHandler.js
Once that's done, I am using the setMutationObserver to addEventListener('click', function(event) {} to each button that has been inserted in to the '._5pcq' class. Which means it's looking inside of each '._3576' class for the buttons after they've been inserted, which means that I'm pointing the second MutationObserver to look for '.TextCopyButton' inside _3576, and for each node(.TextCopyButton) that it finds, it's adding the listener.
Now, in order to copy the text in the '._3576' class. I need to traverse from the event.target which is (.TextCopyButton) up to where the text to be copied is located at. Which is 2 parentNodes up, nextSibling, nextSibling, and then up 9 parentNodes. Without losing track of the current node.
event.target.parentNode.parentNode.nextSibling.nextSibling.parentNode.parentNode.parentNode.parentNode.parentNode.parentNode.parentNode.parentNode.nextSibling;
I can not do getElementsByClassName(_3576), because that will require a node index number. Which means I need to use event.target to reference the current node that the button is clicked from. My question is how can I reference the current node, and get to the location I need to without using so many parentNode chains.
I need something along the lines of event.target.parentNode[2].nextSibling[2].parentNode[9]
OR
event.target.traverseToNode('.3576');
I have no idea what else to try. I have tried getElementsByClassName('_3576')[event.target]
I have tried using the THIS keyword.
My code is located here:
setMutationHandler({
target: document.querySelector('._3576'),
selector: '.copybtnP',
handler: nodes => nodes.forEach(node => {
node.setAttribute("style", "color:blue; border: 1px solid blue;");
node.addEventListener('click', function(event) {
var classCheck = document.getElementsByClassName('canvasArea');
var status_content = event.target.parentNode.parentNode.nextSibling.nextSibling.parentNode.parentNode.parentNode.parentNode.parentNode.parentNode.parentNode.parentNode.nextSibling;
if (classCheck.length > 0) {
status_content = event.target.previousSibling.previousSibling.previousSibling;
}
//alert(status_content + ' (Working)');
//alert(event.target.previousSibling.innerText);
getSelection().removeAllRanges();
var range = document.createRange();
range.selectNode(status_content);
window.getSelection().addRange(range);
try { var successful = document.execCommand('copy');
var msg = successful ? 'successful' : 'unsuccessful';
//console.log('Copying text command was ' + msg);
} catch (err) {console.log('Oops, unable to copy');}
return false;
})
})
});
https://pastebin.com/Et66j0uc
I think you buried your question inside a bunch of unnecessary description. From what I understand, you want to simply reduce the number of .parentNode calls when traversing up the DOM.
The most obvious answer is to create a function that takes the starting point and the number of traversals, and does them in a loop.
function up(start, num) {
while (--num >= 0 && (start = start.parentNode)) {
// nothing to do in the loop body
}
return start
}
const res = up(event.target, 2)
Then you can do the same for sibling traversal.
However, you can also use the .closest() method for going up through the ancestors, assuming you can describe the element targeted with a selector.
const res = event.target.closest(".someParentClass")
This should prove to be more resilient against changes you make to the DOM, assuming your selector is well defined.

JavaScript code execution delay

function append(what) {
$("#drawer").append(what);
}
function outerHtml(o) {
return $("<div />").append($(o).clone()).html();
}
var allPixel = [];
$(".pix").each(function() {
allPixel.push(outerHtml($(this)));
});
$("#drawer").empty();
var index;
for (index = 0; index < allPixel.length; index++) {
pixel = allPixel[index];
setTimeout(append(pixel), 100);
}
I have a Container (#drawer) that is full of many div elements. In this function the HTML of each of these div elements gets saved in an array individually. When its done doing that the div element gets cleared.
In the Array allPixel are now all div elements. I want it so that each div element gets added to #drawer with a delay of 100ms.
Problem:
If I run this function nothing happens (the divs are not disappearing/ appearing). What did I do wrong?
If anything is unclear feel free to let me know and I will edit my question.
Question aswered... See it in action: https://wiese2.lima-city.de/test/
You are invoking the method invoking immediately and passing its return value i.e. undefined to setTimeout.
You can set additional parameters to be passed to append function when timer expires to setTimeout method.
Use
setTimeout(append, 100 * (i + 1), pixel);
Additionally you also need to increase timer based on element index

Using parent() in a for loop

I am creating a chrome extension that blocks all porn results on all torrent search engine sites.
So I am trying to retrieve the name of the torrents and check them against the array of strings containing blocked (adult/porn) words that I created. If it matches the array word then it should set the display of the parent element to none. But parent() from jQuery doesn't seem to work around this in a for loop. This is the code that I am using.
// 'blockedWords' is the array.
// '$("dl dt")' contains the words that I am checking against strings from
// the array 'blockedWords'.
for (var i = 0; i < $("dl dt").length; i++) {
for (var j = 0; j < blockedWords.length; j++) {
if($("dl dt")[i].innerText.indexOf(blockedWords[j]) > -1){
$(this).parent().style.display= "none"; // 1st Method or
$("dl dt")[i].parent().style.display= "none"; // 2nd Method
}
}
}
// 1st Method shows the error 'Cannot set property 'display' of undefined'
// 2nd Method shows the error '$(...)[i].parent is not a function'
// '$("dl dt")[i].parent().style.display' doesn't work but
// '$("dl dt").parent().style.display' doesn't work either
// '$("dl dt")[i].style.display' works perfectly without parent().
I have also tried 'parents()'.
Any help will be appreciated :).
As a newbie, I am also open to any other suggestions or recommendations.
And I would be really grateful if you could explain your code as well :)
And by the way, can you believe there are more than 500 porn companies out there :o :P :D
Since you have jQuery, you can avoid using nested for-loops using jQuery's filter() and JavaScript reduce(s,v):
// Filter function removes elements that return a false/falsey value like 0
$("dl dt").filter(function() {
// Save current element's innerText so we can use it within the reduce function
var str = $(this).text();
// Return sum of reduce function
return blockedWords.reduce(function(s, v) {
// For each item in blockedWords array, check whether it exists in the string. Add to total number of matches.
return s + !!~str.indexOf(v);
}, 0); // 0 = intial value of reduce function (number of matches)
}).parent().hide(); // Hide elements which pass through the filter function
Demo:
var blockedWords = [
'shit', 'fuck', 'sex'
];
$("dl dt").filter(function() {
var str = $(this).text();
return blockedWords.reduce(function(s, v) {
return s + !!~str.indexOf(v);
}, 0);
}).parent().hide();
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<dl><dt>this is shit</dt></dl>
<dl><dt>this is okay</dt></dl>
<dl><dt>fuck this</dt></dl>
<dl><dt>no problem</dt></dl>
<dl><dt>sex videos</dt></dl>
EDIT: I apologize for the earlier answer if you saw it, as it was incomplete. I have also added a snippet for demonstration purposes. For further explanation of the reduce algorithm, check this answer out (basically it converts the value of indexOf to either a 0 or 1, because indexOf returns -1 if not found, or another 0-indexed integer of the position if found).
JQuery's parent function returns a JQuery object with the parent element inside of it. If you want to access the element from this object you need to retrieve the element from the object using the bracket notation.
If you were to provide some HTML I would be able to test this and make sure it works, but here is some code that could get you pointed in the right direction to use mostly JQuery instead of relying on for loops with JavaScript.
JQuery Rewrite
$("dl dt").each(function(index, element){
if($.inArray(blockedWords,$(element).text()) > -1) {
$(this).parent().css("display", "block");
$(element).parent().css("display", "block");
}
})
The Answer To Your Specific Question
Change this:
$(this).parent().style.display= "none"; // 1st Method or
$("dl dt")[i].parent().style.display= "none"; // 2nd Method
to this:
$(this).parent()[0].style.display= "none"; // 1st Method or
$($("dl dt")[i]).parent()[0].style.display= "none"; // 2nd Method
optionally, you can instead use JQuery's css function like this:
$(this).parent().css("display", "none"); // 1st Method or
$($("dl dt")[i]).parent().css("display","none"); // 2nd Method

Trying to make div background change color when 2 ids are true

I am trying to make each div's background change color when 2 ids exist. it is not changing the color. I cannot figure out what I am doing wrong. I am brand new to javascript. I have an embedded stylesheet and dont know if the javascript will override the css.
Also, I know some PHP and want to 'echo' the variables throughout the program so that I can see what the string value is in order to debug my own code. what is the easiest way to do this?
function drop(ev){
ev.preventDefault();
var image = ev.dataTransfer.getData("content");
ev.target.appendChild(document.getElementById(image));
var mydiv = '';
for (var i=0;i<9;i++)
{
if ($('#target'.i).find('#answer'.i).length == 1)
{
mydiv = document.getElementById('target'+i);
mydiv.style.backgroundColor = '#00CC00';
}
else
{
mydiv = document.getElementById('target'+i);
mydiv.style.backgroundColor = '#FF0000';
}
}
}
I think your problem may be on this line you have . not + to build the id's correctly.
if ($('#target'.i).find('#answer'.i).length == 1)
so your code should be:
if ($('#target'+i).find('#answer'+i).length == 1)
Keeping in mind I'm no jQuery wizard, my first notion was something like this:
$('div[id^=target]').each(function() {
var el = $(this).find('div[id^=answer]').addBack();
el.css('backgroundColor', el.length > 1 ? '#00CC00' : '#FF0000');
});
...but then I noticed that unlike your example, I was changing both the parent and child div. Something like this might be closer to your intent:
$('div[id^=target]').css('backgroundColor', function () {
return $(this).find('div[id^=answer]').length ? '#00CC00' : '#FF0000';
});
You also could retain the for loop if that's your preference:
for (var i = 0; i < 9; ++i) {
$('div#target' + i).css('backgroundColor', function() {
return $(this).find('div#answer' + i).length ? '#00CC00' : '#FF0000';
});
}
...and, just for fun, something kinda esoteric:
$('div[id^=target]:has(div[id^=answer])').css('backgroundColor', '#00CC00');
$('div[id^=target]:not(:has(div[id^=answer]))').css('backgroundColor', '#FF0000');
Fiddle!
Your code should work (see fiddle) with the correct operator for concatenation, i.e. with + instead of ., however here are a few points you should bear in mind :
Point 1 :
Among all the i variables you're iterating over in your for loop, if there is no div with id equal to "target" + i you will end up in the following else block :
else
{
mydiv = document.getElementById('target'+i); // null
mydiv.style.backgroundColor = '#FF0000';
}
At that place mydiv will be null and mydiv.style will throw an error.
Point 2 :
It seems you used jQuery to find the answers elements, while you used document.getElementById, which is part of the DOM API, to select then the target element. It would have been more consistent to use jQuery there too.
Point 3 :
If you want to simply output the value of some variable you can use console.log, which will output in the javascript console of the browser. The console object is provided by the browser, therefore you may not have the console.log method, but if you are using an up to date browser there is a good chance you will have it.
To summarize, see this fiddle for an example that takes these points into account.

Categories