繁体   English   中英

如何使用客户端 javascript 将外部/内部 css 样式转换为内联样式属性

[英]How to convert external/internal css styles into inline style-attribute using client-side javascript

例如,如果我有包含在文档中的 CSS:

div {
  color: red;
}
div.test {
  border: 1px solid blue;
}

和文档正文中的 html 标签:

<div id='demo'>
  <div class='test'>123</div>
  <div>456</div>
</div>

我想将#demo中的所有内容都转换为一个字符串,其中包含它使用的所有样式的标签,例如:

var parent = $('#demo');
var result = convertExternalInternalStylesToInline(parent);

// the desired result:
result = '<div style="color: red; border: 1px solid blue;">123</div>' +
         '<div style="color: red">456</div>'

我需要的是convertExternalInternalStylesToInline函数的内容,它自动获取所有后代元素,应用计算/使用的样式,然后将 css 样式添加到这些元素,然后将其全部作为 html 字符串返回。

可以仅使用客户端javascript来完成吗? 如果是,如何?

(我需要知道如何获取标签的所有计算/使用的样式)

最小的例子:

function convertExternalInternalStylesToInline(parent) {
  var parent = $(parent).clone(); // clone it before modify, is this deep clone?
  parent.find('*').each(function(idx,el){ // fetch all children
     var el = $(el); // convert to jquery object
     // get all applied styles on this element
     // el.?                        // --> don't know how
     // apply css for each styles
     el.css( css_prop, css_style );
  });
  // return as string, maybe:
  return parent.html();
}

编辑:感谢Winchestro,我查找了window.getMatchedCSSRules函数,该函数实际上仅在Webkit中存在,并且有一个讨论,建议不要使用它

您实际上应该使用的是window.getComputedStyle() 阅读MDN上的文档

您可能要研究的另一个非常有用的资源是CSSUtilities库集


您需要做的是两件事,一个库来解析CSS对象模态(CSSOM),然后将相关CSS应用于DOM元素。

我知道一个很好的nodejs库,它叫做Juice

我发现有一个库可能在前端工作,称为inlineresources ,它具有浏览器化的版本

再三考虑,我想您也许可以将Juice和browserify一起使用...但是您必须手动评估这种可能性...

假设你有一个数组中的 HTML(行:string[]):

我不知道为什么我花了这么多时间在这上面

let styleLines = this.getLinesInsideStyleTag(lines)
let htmlLinesSeparatedByTag = this.getLinesNotInsideStyleTag(lines)
let mapOfCSSrules = this.getMapOfCSSRuleNamesAndPropertiesFromCSSLines(styleLines)
let linesWithInlineRulesApplied = this.applyInlineStylesToLines(htmlLinesSeparatedByTag, mapOfCSSrules)

let finalString = ""
for(let v = 0; v < linesWithInlineRulesApplied.length; v++ ) {
    finalString = finalString + linesWithInlineRulesApplied[v]   
}

console.log(finalString)

    getLinesInsideStyleTag(lines: any[]) {
        let styleLines: any[] = []
        let foundStylesStartTag = false
        let foundStylesEndTag = false
        for(let i = 0; i < lines.length; i++ ) {
            if(lines[i].indexOf("<style ") != -1) {
                foundStylesStartTag=true
            } else if(lines[i].indexOf("</style>") != -1) {
                foundStylesEndTag = true
            }

            if(foundStylesStartTag == true && foundStylesEndTag == false) {
                if(lines[i].indexOf("<style") == -1 && lines[i].indexOf("</style") == -1) {
                    styleLines.push(lines[i])
                }
            } 
            // console.log("lines[i] after: ")
            // console.log(lines[i])
        }
        return styleLines
    }



    getLinesNotInsideStyleTag(lines: any[]) {
                                    
        let foundStylesStartTag = false
        let foundStylesEndTag = false

        let linesToKeep: any[] = []
        for(let i = 0; i < lines.length; i++ ) {
            if(lines[i].indexOf("<style ") != -1) {
                foundStylesStartTag=true
            } else if(lines[i].indexOf("</style>") != -1) {
                foundStylesEndTag = true
            }

            if(foundStylesStartTag == false && foundStylesEndTag == false) {
                linesToKeep.push(lines[i])
            } else if(foundStylesStartTag == true && foundStylesEndTag == true) {
                if(lines[i].indexOf("<style") == -1 && lines[i].indexOf("</style") == -1) {
                    linesToKeep.push(lines[i])
                }
            }
        }

        let actualLinesToKeep: any[] = []
        for(let i = 0; i < linesToKeep.length; i++ ){ 
            let thisLineSplitOnOpeningTag = linesToKeep[i].split("<")
            let pushFullLine = false
            let modifiedLine = ""
            for(let y = 0; y < thisLineSplitOnOpeningTag.length; y++) {
                if(thisLineSplitOnOpeningTag[0] !== "") {    
                    pushFullLine = true
                } else {
                    if(thisLineSplitOnOpeningTag.length > 2) {
                        //then the line contains nested tags (oof)
                        if(thisLineSplitOnOpeningTag[y].length > 0) {
                            if( y != thisLineSplitOnOpeningTag.length - 1) {
                                modifiedLine = modifiedLine + "<" + thisLineSplitOnOpeningTag[y]+"%*#"
                            } else {
                                modifiedLine = modifiedLine + "<" + thisLineSplitOnOpeningTag[y]
                            }
                        }
                    } else {
                        pushFullLine = true
                    }
                }
            }

            if(pushFullLine == true) {
                // console.log("3pushing full line because it doesn't have nested tags: "+linesToKeep[i])
                actualLinesToKeep.push(linesToKeep[i])
            } else {
                actualLinesToKeep.push(modifiedLine)
            }
        }
        // console.log("actualLinesToKeep: ")
        // console.log(actualLinesToKeep)
        return actualLinesToKeep
    }



    //e.g. you pass it 
    // myRule {
    //    color: blue;
    //    text-align: left;
    // }
    // you get back: a dictionary / map where "myRule" is the key, and "color: blue;" and "text-align: left;" are the values for that key.
    getMapOfCSSRuleNamesAndPropertiesFromCSSLines(styleLines: any[]) {
        // console.log("styleLines: ")
        // console.log(styleLines)

                        //rule, properties
        let CSSrules: Map<string,any[]> = new Map();

        let rulesSplitOnClosingBracket = styleLines.toString().split("}")

        for(let i = 0; i < rulesSplitOnClosingBracket.length; i++) {
            let indexOfOpeningBracket = rulesSplitOnClosingBracket[i].indexOf("{")
            let ruleName = rulesSplitOnClosingBracket[i].substring(0,indexOfOpeningBracket).trim()
            ruleName = this.replaceAll(ruleName,",","").toLowerCase()
            if(ruleName[0] === ".") {
                ruleName = ruleName.substring(1)
            }
            //replace dots with a space
            ruleName = ruleName.replace(/\./g,' ')
            let propertiesOfThisRule = rulesSplitOnClosingBracket[i].substring(indexOfOpeningBracket+1).split(",")
            let propertiesToKeep: any[] = []
            for(let j = 0; j < propertiesOfThisRule.length; j++) {
                propertiesOfThisRule[j] = propertiesOfThisRule[j].trim()
                if(propertiesOfThisRule[j] !== undefined && propertiesOfThisRule[j].length > 0) {
                    propertiesToKeep.push(propertiesOfThisRule[j])
                }
            }

            if(ruleName !== undefined && ruleName.length > 0 && propertiesToKeep !== undefined && propertiesToKeep.length > 0) {
                CSSrules.set(ruleName, propertiesToKeep)
            }
        }
        return CSSrules
    }


applyInlineStylesToLines(htmlLinesSeparatedByTag: any[], mapOfCSSrules: Map<string,any[]>) {
        let linesWithInlineRulesApplied: any[] = []
        let ruleNames = Array.from(mapOfCSSrules.keys())

        for(let r = 0; r < htmlLinesSeparatedByTag.length; r++) {
            let lineSplitOnContinuationCharacter = htmlLinesSeparatedByTag[r].split("%*#")
            let partsOfLineThatContainClosingTags: any[] = []
            let partsOfLineThatDoNotContainClosingTags: any[] = []
            for(let d = 0; d < lineSplitOnContinuationCharacter.length; d++) {
                if(lineSplitOnContinuationCharacter[d].indexOf("</") != -1) {
                    partsOfLineThatContainClosingTags.push({orderNumber: d, line: lineSplitOnContinuationCharacter[d]})
                } else if(lineSplitOnContinuationCharacter[d].indexOf("</") == -1) {
                    partsOfLineThatDoNotContainClosingTags.push({orderNumber: d, line: lineSplitOnContinuationCharacter[d]})
                }
            }

            let orderNumbers1: any[number] = partsOfLineThatDoNotContainClosingTags.map(val => val.orderNumber)
            let orderNumbers2: any[number] = partsOfLineThatContainClosingTags.map(val => val.orderNumber)
            let maxOrderNumberFor1 = Math.max.apply(Math,orderNumbers1)
            let maxOrderNumberFor2 = Math.max.apply(Math,orderNumbers2)

            let maxOrderNumber: number;
            if(maxOrderNumberFor1 > maxOrderNumberFor2) {
                maxOrderNumber = maxOrderNumberFor1
            } else {
                maxOrderNumber = maxOrderNumberFor2
            }

            let thisActualLineWithStylesApplied = ""
            for(let u = 0; u < maxOrderNumber+1; u++) {
                let partOfLineWithoutClosingTag = partsOfLineThatDoNotContainClosingTags.filter(val => val.orderNumber == u)[0]?.line
                let partOfLineWithClosingTag =  partsOfLineThatContainClosingTags.filter(val => val.orderNumber == u)[0]?.line

                if ( partOfLineWithoutClosingTag !== undefined ) {
                    let idxOfFirstSpace = partOfLineWithoutClosingTag.indexOf(" ")
                    for(let s = 0; s < ruleNames.length; s++) {
                        let applyThisRuleToThisLine = true
                        let textToCheckFor: any[] = ruleNames[s].split(" ")
                        for(let t = 0; t < textToCheckFor.length; t++) {
                            if(partOfLineWithoutClosingTag.indexOf(textToCheckFor[t]) == -1) {
                                applyThisRuleToThisLine = false
                            }
                        }

                        if(applyThisRuleToThisLine) {
                            let lineAfterApplyingStyle = partOfLineWithoutClosingTag.substring(0, idxOfFirstSpace) +" style=\""
                            for(let u = 0; u < mapOfCSSrules.get(ruleNames[s]).length; u++) {
                                let thisPropertyToApply = mapOfCSSrules.get(ruleNames[s])[u]
                                lineAfterApplyingStyle=lineAfterApplyingStyle+thisPropertyToApply
                            }
                            lineAfterApplyingStyle = lineAfterApplyingStyle +"\""
                            partOfLineWithoutClosingTag = lineAfterApplyingStyle + partOfLineWithoutClosingTag 

                            let lastIndexOfLessThan = partOfLineWithoutClosingTag.lastIndexOf("<")
                            let lastIndexOfGreaterThan = partOfLineWithoutClosingTag.lastIndexOf(">")
                            partOfLineWithoutClosingTag = partOfLineWithoutClosingTag.substring(0,lastIndexOfLessThan) + partOfLineWithoutClosingTag.substring(lastIndexOfGreaterThan)
                        }
                    } 
                    thisActualLineWithStylesApplied = thisActualLineWithStylesApplied + partOfLineWithoutClosingTag
                }
                if(partOfLineWithClosingTag !== undefined) {
                    thisActualLineWithStylesApplied = thisActualLineWithStylesApplied + partOfLineWithClosingTag
                } 
            }
            linesWithInlineRulesApplied.push(thisActualLineWithStylesApplied)
        }
        return linesWithInlineRulesApplied
    }





编辑:感谢Kumar,我意识到我建议的方法是非标准的,并且

getComputedStyle( element );

应该改用,这会更难使用和过滤,但是具有的优点是,仅提供评估CSS后实际应用于元素的最终规则(还包括默认样式,而不是显式声明的样式,从而使其在这种情况下使用起来有点棘手)。


getMatchedCSSRules( element );

只需使用此香草javascript函数即可。 它正是您想要的。 如果有任何要解释的功能名称未暗示的内容,我将进行解释。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM