Converty jQuery closest to ES6 to add CSS for bg - javascript

Objective
Get the url of each image and add that as a background to it's parent article, in a vuejs project
Question
How should I refactor the code originally written in jQuery?
JS - original from other author
$(".polaroid").wrap("<div class='polaroid-frame'><div class='polaroid-image'>");
$(".polaroid").each(function() {
var $this = $(this);
var imgLink = $this.attr("src");
var bg = "url("+imgLink+")";
$this.closest(".polaroid-image").css({
"background-image": bg
});
});
JS - refactored for my project
$(".polaroid article img").each(function() {
var $this = $(this);
var imgLink = $this.attr("src");
var bg = "url("+imgLink+")";
$this.closest("article").css({
"background-image": bg
});
});
My demo on codepen
Background
I am taking code from another project but it was written in jQuery. I don't want to use jQuery, instead i just need to write this in ES6, or just plain javascript.
I so far found one related question, Finding closest element without jQuery , but it was too different to be able to make much use of it.
I looked at the mozilla documentation https://developer.mozilla.org/en-US/docs/Web/API/Element/closest , which leads me to think my code should translate to
el.closest("article");
but then how would I wrap that in a way that would pass the CSS values. I looked up documentation from w3 https://www.w3schools.com/js/js_htmldom_css.asp and found this example
document.getElementById("p2").style.color = "blue";
So I think I should do
document.[unclear what to do here].style.background-image = /* what goes here for the variable? */;

From your refactored JS, to translate it into plain JS, you need to
(1) Iterate over the selected elements (can be done easily with querySelectorAll)
(2) Get the element's src attribute
(3) Set the closest article's background-image to that src
So, it should be pretty simple:
document.querySelectorAll('.polaroid article img').forEach((img) => {
const { src } = img;
img.closest('article').style.backgroundImage = `url(${src})`;
});
Note that when setting style properties directly, sometimes the CSS string you'd want to use (eg. background-image) wouldn't be valid syntax in Javascript. In these cases, generally turn the words-separated-by-dashes into camelCase. (backgroundImage)

Related

Find all divs/elements with background image containing URL and remove text

I read this Replacing background image using jQuery not working and quite a few other things but I'm having issues with this one.
I'm trying to find all divs/elements that contain a background-image: url();. IF they contain a background image with https://website.com/imagepath/image.jpg I want to remove the "https://website.com" piece and leave it as a local url only i.e. "/imagepath/image.jpg"
I'd like to do this in pure JS but I'm not opposed to jQuery
This would be incredibly taxing on performance and I would not recommend doing it this way.
You would have to go through every single element on the page and check its computed style and then update that if it matches your provided string.
const searchStr = 'https://website.com/imagepath/image.jpg';
const replaceStr = 'https://website.com/newimage.jpg';
const body = document.getElementsByTagName('body')[0];
const elements = Array.from(body.getElementsByTagName('*'));
elements.map(element => {
const style = window.getComputedStyle(element);
if (!!style.backgroundImage.includes(searchStr)) {
element.style.backgroundImage = replaceStr;
}
});

How to set all computed styles as literal styles on an element?

This question is similar (I think) to this question on SO: How to move all computed CSS styles from one element and apply them to a different element using JavaScript?
What I want to do is find all styles that have been applied by way of css sheets and set the explicitly (as in using a style= attribute) on the element. The reason I want to do this is so that MS Word (2010) will pick up the styling if I open it there
Here's my code
var $this = $(this);
var styles = window.getComputedStyle(this, null);
$this.css(styles);
I also tried with
var styles = document.defaultView.getComputedStyle(this, null);
When I put a try catch around the last line ($this.css(styles), I am getting an error message "invalid calling object". I don't understand what the problem is
Well, the following snippet does what you want, though I don't think it's what you needed. MS Word is NOT a browser, I'm not sure if it supports all the styles or if jQuery works there...
var $this = $('b');
var styles = window.getComputedStyle($this[0]);
$this.css(
Object.values(styles).reduce(
(a, e) => 0*(a[e] = styles[e]) || a, {}
)
)
console.log(`style="${$this.attr('style')}"`)
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<b>I have styles</b>

Programatically create new CSS rules in a stylesheet

For creating new CSS rules in a stylesheet, as of now, I only know .insertRule() (excluding the non-standard ways).
Are there any other ways of creating more CSS rules? Something, for example:
Warning: this is not standard, it is just an example to display the intent
var rule = new CSSStyleRule();
rule.selectorText = '.selector';
rule.style.display = 'block';
rule.style.color = 'red';
ruleSet.append(rule);
Anything like or somewhat like the above works as an answer. Instead of new, it may be document.createCssRule() or stylesheet.createCssRule()...
I just this it would be useful for a piece of software I'm developing which left me if there's a programatically way of modifying values in such interface where to add new stuff is not restricted to a parsed string.
Do not worry about IE9 and below when answering, please.
Note: (to unclose) This is not about how to find and modify current CSS rules, this is about making new ones without using the text parser which is what .insertRule() is without building a complete CSS string like .insertRule() requires.
You can add an empty rule, get it, and modify it:
function appendRule(sheet) {
var len = sheet.cssRules.length;
sheet.insertRule('*{}', len);
return sheet.cssRules[len];
}
var rule = appendRule(document.styleSheets[0]);
rule.selectorText = '.selector';
rule.style.display = 'block';
rule.style.color = 'red';
<span class="selector">I should become red.</span>
<span>I should be at the next line and not become red.</span>
However, modifying selectorText does not seem to work on Firefox.
You could append a new style element to your head and just append rules to it:
function addStyle(newRules){
if(document.getElementById("jsAddedRules")){
document.getElementById("jsAddedRules").innerHTML+=newRules;
}else{
var style=document.createElement("style");
style.setAttribure("id","jsAddedRules");
style.innerHTML=newRules;
document.getElementsByTagName("head")[0].appendChild(style);
}
}
And then just call your function when you want to add rules:
addStyle(
".selector{\
display:block;\
color:red;\
}"
);

Select tags that starts with "x-" in jQuery

How can I select nodes that begin with a "x-" tag name, here is an hierarchy DOM tree example:
<div>
<x-tab>
<div></div>
<div>
<x-map></x-map>
</div>
</x-tab>
</div>
<x-footer></x-footer>
jQuery does not allow me to query $('x-*'), is there any way that I could achieve this?
The below is just working fine. Though I am not sure about performance as I am using regex.
$('body *').filter(function(){
return /^x-/i.test(this.nodeName);
}).each(function(){
console.log(this.nodeName);
});
Working fiddle
PS: In above sample, I am considering body tag as parent element.
UPDATE :
After checking Mohamed Meligy's post, It seems regex is faster than string manipulation in this condition. and It could become more faster (or same) if we use find. Something like this:
$('body').find('*').filter(function(){
return /^x-/i.test(this.nodeName);
}).each(function(){
console.log(this.nodeName);
});
jsperf test
UPDATE 2:
If you want to search in document then you can do the below which is fastest:
$(Array.prototype.slice.call(document.all)).filter(function () {
return /^x-/i.test(this.nodeName);
}).each(function(){
console.log(this.nodeName);
});
jsperf test
There is no native way to do this, it has worst performance, so, just do it yourself.
Example:
var results = $("div").find("*").filter(function(){
return /^x\-/i.test(this.nodeName);
});
Full example:
http://jsfiddle.net/6b8YY/3/
Notes: (Updated, see comments)
If you are wondering why I use this way for checking tag name, see:
JavaScript: case-insensitive search
and see comments as well.
Also, if you are wondering about the find method instead of adding to selector, since selectors are matched from right not from left, it may be better to separate the selector. I could also do this:
$("*", $("div")). Preferably though instead of just div add an ID or something to it so that parent match is quick.
In the comments you'll find a proof that it's not faster. This applies to very simple documents though I believe, where the cost of creating a jQuery object is higher than the cost of searching all DOM elements. In realistic page sizes though this will not be the case.
Update:
I also really like Teifi's answer. You can do it in one place and then reuse it everywhere. For example, let me mix my way with his:
// In some shared libraries location:
$.extend($.expr[':'], {
x : function(e) {
return /^x\-/i.test(this.nodeName);
}
});
// Then you can use it like:
$(function(){
// One way
var results = $("div").find(":x");
// But even nicer, you can mix with other selectors
// Say you want to get <a> tags directly inside x-* tags inside <section>
var anchors = $("section :x > a");
// Another example to show the power, say using a class name with it:
var highlightedResults = $(":x.highlight");
// Note I made the CSS class right most to be matched first for speed
});
It's the same performance hit, but more convenient API.
It might not be efficient, but consider it as a last option if you do not get any answer.
Try adding a custom attribute to these tags. What i mean is when you add a tag for eg. <x-tag>, add a custom attribute with it and assign it the same value as the tag, so the html looks like <x-tag CustAttr="x-tag">.
Now to get tags starting with x-, you can use the following jQuery code:
$("[CustAttr^=x-]")
and you will get all the tags that start with x-
custom jquery selector
jQuery(function($) {
$.extend($.expr[':'], {
X : function(e) {
return /^x-/i.test(e.tagName);
}
});
});
than, use $(":X") or $("*:X") to select your nodes.
Although this does not answer the question directly it could provide a solution, by "defining" the tags in the selector you can get all of that type?
$('x-tab, x-map, x-footer')
Workaround: if you want this thing more than once, it might be a lot more efficient to add a class based on the tag - which you only do once at the beginning, and then you filter for the tag the trivial way.
What I mean is,
function addTagMarks() {
// call when the document is ready, or when you have new tags
var prefix = "tag--"; // choose a prefix that avoids collision
var newbies = $("*").not("[class^='"+prefix+"']"); // skip what's done already
newbies.each(function() {
var tagName = $(this).prop("tagName").toLowerCase();
$(this).addClass(prefix + tagName);
});
}
After this, you can do a $("[class^='tag--x-']") or the same thing with querySelectorAll and it will be reasonably fast.
See if this works!
function getXNodes() {
var regex = /x-/, i = 0, totalnodes = [];
while (i !== document.all.length) {
if (regex.test(document.all[i].nodeName)) {
totalnodes.push(document.all[i]);
}
i++;
}
return totalnodes;
}
Demo Fiddle
var i=0;
for(i=0; i< document.all.length; i++){
if(document.all[i].nodeName.toLowerCase().indexOf('x-') !== -1){
$(document.all[i].nodeName.toLowerCase()).addClass('test');
}
}
Try this
var test = $('[x-]');
if(test)
alert('eureka!');
Basically jQuery selector works like CSS selector.
Read jQuery selector API here.

Count the number of classes an element has?

If you were to label elements by some [tag] system, likely attached by the user, and you wanted to count the number of tags (defined by the number of classes the element has), how would you accomplish this?
This could be beneficial if you were to try to review all elements by number of tags. (This likely could be accomplished by other structures, but if you were to only reference the element tags in this way)
Jquery has .hasClass(), is there something like .classCount()
You could create it...
$.fn.classCount = function() {
return $.grep(this.attr("class").split(/\s+/), $.trim).length;
};
jsFiddle.
If you don't have jQuery, you could use...
var classLength = element.classList.length;
jsFiddle.
If you don't have jQuery and have to support the older browsers, go with...
var classes = element.className.replace(/^\s+|\s+$/g, "").split(/\s+/);
var classLength = classes[0].length ? classes.length : 0;
jsFiddle.
You can use the classList attribute if you are using HTML5. For example
$("#someElementId").classList.length
would return 2 for:
<div id="someElementId" class="stinky cheese"></div>
as in http://jsfiddle.net/thegreatmichael/rpdEr/
$(element).attr('class').split(/\s+/).length
jsFiddle example

Categories