用JS来修改网页属性

昨天要解决一个 bug,在手机端网页中的视频框溢出。

问题由来

要在网页中插入视频时,现在一般的做法是,把视频托管在第三方服务,第三方服务会提供一段插入视频的代码。比如说 POLYV,会提供这样一段代码:

<script src='//player.polyv.net/script/polyvplayer.min.js'></script>

<div id='plv_a49c512d573f5e4b9444e1c90ce88080_a'></div>

<script>
var player = polyvObject('#plv_a49c512d573f5e4b9444e1c90ce88080_a').videoPlayer({
   'width':'600',
  'height':'338',
    'vid' : 'a49c512d573f5e4b9444e1c90ce88080_a'
});
</script>

第一段代码是引入 POLYV 的播放器插件,第二段是该视频的插入位置,代一个随机的视频id,第三段是生成播放视频的 JavaScript 代码。

这是 POLYV 提供的代码,只需要贴到网页里,就可以把视频引入了。注意到第三段代码里,对视频的播放框设定了宽度和长度。在一般的网页浏览是没问题的,当在小屏幕浏览时,比如说手机里,视频的宽度无法自适应,仍然是600宽,就会溢出。

74600EF7-D9A4-4A71-A259-B604DA1D7BBD

解决思路

这个问题的由来,其实就是这段代码里限制死了视频播放框的尺寸,最简单的解决办法,就是在这段代码里加一句

'max-width':'100%',

但是这个解决办法不是负责任的办法。因为这段代码是由 POLYV 提供,然后由用户贴到文章里的。要求用户每次贴视频的时候都修改代码,这简直是不可能的。而且从产品的角度来说,这是一个 bug

所以,必须要在现有的条件下来解决。换言之,就是要想办法为视频播放框加一个属性 max-width:100%

问题研究

第一步,当然是要分析网页的 HTML 代码。用 Chrome 的 inspect 查看。

在桌面端,最后生成的代码是

<div id='plv_a49c512d573f5e4b9444e1c90ce88080_a'>
  <object type='application/x-shockwave-flash' data="//player.polyv.net/videos/player.swf" id="a49c512d573f5e4b9444e1c90ce88080_a" width="600" height="338" class="polyvFlashObject">
    //更多代码
  </object>
</div>

看起来问题很简单,于是我就在 css 文件里为 object 加了一条属性

object {
  max-width:100%;
}

在 Chrome 中切换到手机界面,发现起作用了,视频框被限制在了 100% 的宽度,没有再溢出。问题解决了?

当我刷新时,发现视频再次溢出了。再看网页的代码,发现竟然没有了 object 这个标签!代码变成了

<div id='plv_a49c512d573f5e4b9444e1c90ce88080_a'>
  <div id='containera49c512d573f5e4b9444e1c90ce88080_a' style="height: 338px; width: 600px; position: relative; max-width: 100%;">
    <iframe name="polyvPlayer" //... >
      //更多代码
    </iframe>
  </div>
</div>

细想之下,肯定是 POLYV 的播放器作的手脚,在桌面端,播放器用的是 flash,在手机端,就是嵌入一个 iframe 来实现。两种形式,最后呈现的 HTML 代码是不一样的。

经过在 chrome 里反复测试,只有在 id='containera49c512d573f5e4b9444e1c90ce88080_a' 这一层加入宽度限制的属性,才能把视频播放框的宽度限制住。但这个 id 是不确定的,这样就没办法提前在 CSS 文件里作限制。只能用 JavaScript 了。

现在的问题变成:

  1. 在网页生成之后,找出包裹视频的那个 id
  2. 给这个 id 加一个限制宽度的属性。

解决方案一

注意到这一段代码

<script>
var player = polyvObject('#plv_a49c512d573f5e4b9444e1c90ce88080_a').videoPlayer({
   'width':'600',
  'height':'338',
    'vid' : 'a49c512d573f5e4b9444e1c90ce88080_a'
});
</script>

定义了一个变量 player ,里面包含了一个 vid 的属性。

而我要找的 idcontainera49c512d573f5e4b9444e1c90ce88080_a,实际上就是由 'container' + vid 组成的。这就好办了。先在网页的 console 里先试试。

var id = "#container" + player.vid
$(id).css('max-width', "100%")

02670822-DC8F-4481-B667-D8D52649C934

起作用了。

只要在网页加载之后,再运行这两行 JavaScript 代码就可以了。

最后的代码是这样的:

$(document).on('turbolinks:load', function() {
  var id = "#container" + player.vid;
  $(id).css('max-width', "100%");
}

解决方案二

上面的解决方案有一个问题:当一个页面插入两个视频的时候,第一个视频就失效了。

当用相同的代码插入两个或两个以上视频时,player 这个变量指代的其实只是最后一个视频。也就是说,player 只是一个中间变量,依靠中间变量来修改是不稳妥的。

怎样才更稳妥呢?最好能**找出网页中所有以 container 开头的 id **。

这里要用到 JQuery 的一个模糊查找功能

The [attribute^=value] selector selects each element with a specific attribute, with a value beginning in a specific string.

$(document).on('turbolinks:load', function() {
  var vid=$('div[id^=container]');
  if (vid.length > 0) {
    for (var i = 0; i < vid.length; i++) {
      $(vid[i]).css('max-width', '100%');
    }
  };
}

这里 $('div[id^=container]') 的意思就是提取出所有带有以 container 开头的 id 的 div 标签,结果是一个 array,然后再通过一个循环,把它们都加一个 css 样式。

总结

之前除了用 JavaScript 的插件,很少自己通过写 JS 代码来直接解决问题。这一次的解 bug 过程让我对 JS 的使用有了进一步的理解。主要学到的知识点包括

  1. 在网页加载之后,用 JS 来修改网页;
  2. 为带有某个 id 的标签增加 CSS 样式;
  3. JQuery 的模糊查找功能。
· JavaScript, rails